智能小程序 Ray 开发面板 SDK ——智能设备模型拦截器logger和dp-kit接入指南

logger

日志 console 打印拦截器,类似 redux-logger。

接入指南

根据业务场景生成 dp-kit

src/devices/index.ts

import { SmartDeviceModel, logger } from '@ray-js/panel-sdk';
import { defaultSchema } from '@/devices/schema';
 
type SmartDeviceSchema = typeof defaultSchema;
 
const options = {
  interceptors: {
    request: {
      publishDps: [logger],
    },
    response: {
      onDpDataChange: [logger],
      onDeviceOnlineStatusUpdate: [logger],
      onDeviceInfoUpdated: [logger],
      onNetworkStatusChange: [logger],
      onBluetoothAdapterStateChange: [logger],
    },
  },
};
 
export const devices = {
  common: new SmartDeviceModel<SmartDeviceSchema>(options),
};

API

export declare const logger: Interceptor<any, any, any>;

👉 立即免费领取开发资源,体验涂鸦 MiniApp 小程序开发。  

dp-kit

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

轻量的设备面板 DP 功能点处理工具库,内置支持解析 & 反解析复杂类型 DP 功能点,下发节流、防抖、过滤等功能。

接入指南

配置产品功能点描述文件

src/devices/schema.ts

可参考 创建 SDM

(可选)配置产品协议描述文件

src/devices/protocols.ts

  • 可通过 DSL 或自定义解析器进行配置(若当前产品不存在复杂 DP 数据,可以跳过)
import dpParser from './parsers';
 
export const protocols = {
  colour_data: [
    {
      name: 'hue' as const,
      bytes: 2,
      defaultValue: 0,
    },
    {
      name: 'saturation' as const,
      bytes: 2,
      defaultValue: 1,
    },
    {
      name: 'value' as const,
      bytes: 2,
      defaultValue: 1,
    },
  ],
  control_data: dpParser.controlTransformer,
};

根据业务场景生成 dp-kit

src/devices/index.ts

import { SmartDeviceModel } from '@ray-js/panel-sdk';
import { createDpKit } from '@ray-js/panel-sdk/lib/sdm/interceptors/dp-kit';
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),
};

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

src/app.tsx

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

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

typings/sdm.d.ts

  • 直接复制以下代码片段替换即可,从 devices 目录下获取全局功能点及协议自动声明(若不需要 TS 类型自动提示则可以跳过)
import '@ray-js/panel-sdk';
import { GetStructuredDpState, GetStructuredActions } from '@ray-js/panel-sdk';
 
type SmartDeviceSchema = typeof import('@/devices/schema').defaultSchema; // 注意变量名
type SmartDeviceStructuredProtocols =
  typeof import('@/devices/protocols').protocols;
type SmartDevices =
  import('@ray-js/panel-sdk').SmartDeviceModel<SmartDeviceSchema>;
 
declare module '@ray-js/panel-sdk' {
  export const SdmProvider: React.FC<{
    value: SmartDeviceModel<SmartDeviceSchema>;
    children: React.ReactNode;
  }>;
  export type SmartDeviceInstanceData = {
    devInfo: ReturnType<SmartDevices['getDevInfo']>;
    dpSchema: ReturnType<SmartDevices['getDpSchema']>;
    network: ReturnType<SmartDevices['getNetwork']>;
    bluetooth: ReturnType<SmartDevices['getBluetooth']>;
  };
  export function useProps(): SmartDevices['model']['props'];
  export function useProps<Value extends any>(
    selector: (props?: SmartDevices['model']['props']) => Value,
    equalityFn?: (a: Value, b: Value) => boolean,
  ): Value;
  export function useStructuredProps(): GetStructuredDpState<SmartDeviceStructuredProtocols>;
  export function useStructuredProps<Value extends any>(
    selector: (
      props?: GetStructuredDpState<SmartDeviceStructuredProtocols>,
    ) => Value,
    equalityFn?: (a: Value, b: Value) => boolean,
  ): Value;
  export function useDevice(): SmartDeviceInstanceData;
  export function useDevice<Device extends any>(
    selector: (device: SmartDeviceInstanceData) => Device,
    equalityFn?: (a: Device, b: Device) => boolean,
  ): Device;
  export function useActions(): SmartDevices['model']['actions'];
  export function useStructuredActions(): GetStructuredActions<SmartDeviceStructuredProtocols>;
}

复杂 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>
  );
}

👉 立即免费领取开发资源,体验涂鸦 MiniApp 小程序开发。  

API

export declare const createDpKit: <S extends ReadonlyDpSchemaList>(
  option?: CreateDpKitOptions<S>,
) => CreateDpKitResult;

Result

export type CreateDpKitResult = {
  /**
   * 初始化设备状态,在需要搭配 useStructuredProps 时须在设备模型初始化完毕后调用
   *
   * @param device 设备模型
   */
  init: (device: SmartDeviceModel<any>) => void;
  /**
   * dp-kit 默认拦截器配置
   */
  interceptors: SmartDeviceModelOptions['interceptors'];
  /**
   * dp-kit onDeviceInfoUpdated 拦截器,如需单独搭配使用时可考虑
   */
  onDeviceInfoUpdated: OnDeviceInfoUpdatedInterceptor;
  /**
   * dp-kit onDpDataChange 拦截器,如需单独搭配使用时可考虑
   */
  onDpDataChange: OnDpDataChangeInterceptor;
  /**
   * dp-kit publishDps 拦截器,如需单独搭配使用时可考虑
   */
  publishDps: PublishDpsInterceptor;
};

Params

export type CreateDpKitOptions<
  S extends ReadonlyDpSchemaList,
  SDMDpState = GetSmartDeviceModelProps<S>
> = {
  /**
   * DP 解析协议
   *
   * 当然您也可以使用自定义的规则
   */
  readonly protocols?: Partial<Record<keyof SDMDpState, EnhancedDpMap>>;
  /**
   * DP 下发选项
   */
  readonly sendDpOption?: SendDpOption<SDMDpState>;
  /**
   * 下发 DP 前的钩子
   */
  readonly onBeforeSendDp?: (
    /**
     * 当次下发的 DP
     */
    dpState: Partial<SDMDpState>
  ) => any;
  /**
   * 下发 DP 前的钩子
   */
  readonly onAfterSendDp?: (
    /**
     * 当次下发的 DP
     */
    dpState: Partial<SDMDpState>
  ) => any;
};

(可选)protocols

指定应用里的协议型 dp 的解析方式,将自动解析 & 反解析这类 dp,无需再在业务代码里进行处理。

const dpKit: Middleware = createDpKit<SmartDeviceSchema>({
  protocols: {
    colour_data: [
      {
        name: 'hue' as const, // 注意 name 需要断言一下
        bytes: 2,
        extension: {
          min: 0,
          max: 360,
        },
        defaultValue: 0,
      },
      {
        name: 'saturation' as const,
        bytes: 2,
        extension: {
          min: 0,
          max: 1000,
        },
        defaultValue: 0,
      },
      {
        name: 'brightness' as const,
        bytes: 2,
        extension: {
          min: 0,
          max: 1000,
        },
        defaultValue: 0,
      },
    ],
 
    custom_raw_dp: {
      parser: (dpValue) => {
        // 自定义解析
      },
 
      formatter: (dpValue) => {
        // 自定义格式化
      }
    }
  },
});
 
/**
 * 获取 colour_data 功能点数据
 * useProps(props => props.colour_data) = 000003e803e8
 * useStructuredProps(props => props.colour_data) = { hue: 0, saturation: 1000, brightness: 1000 }
 */
 
/**
 * 下发 colour_data 功能点数据
 * const actions = useActions(); => actions.colour_data.set('000003e803e8');
 * const actions = useStructuredActions(); => actions.colour_data.set({ hue: 0, saturation: 1000, brightness: 1000 });
 * /

(可选)sendOptions

下发 dp 的选项,若配置在 createDpKit 中,将影响后续所有的下发指令。

export type SendDpOption<DpState = any> = {
  /**
   * 是否立即触发 state 更新,必须依赖 dp-kit 拦截器才可使用
   *
   * @default false
   */
  readonly immediate?: boolean;
  /**
   * 忽略 dp 上报
   * @default false
   * @version @ray-js/panel-sdk@1.11.0 开始支持
   */
  readonly ignoreDpDataResponse?: boolean | IgnoreDpChangeInterceptorOptions;
  /**
   * dpData 和云端设备属性同步
   * @default false
   * @version @ray-js/panel-sdk@1.11.0 开始支持
   */
  readonly synchronizeDevProperty?: boolean | DevPropInterceptorOptions;
  /**
   * 多个 DP 是否按对象里的顺序下发,必须依赖 dp-kit 拦截器才可使用
   *
   * @default false
   */
  readonly ordered?: boolean;
  /**
   * 是否进行重复值判断不下发,必须依赖 dp-kit 拦截器才可使用
   * 与当前 `dpState` 进行比较,重复值检出不下发
   *
   * @default false
   */
  readonly checkRepeat?: boolean;
  /**
   * 延迟下发,必须依赖 dp-kit 拦截器才可使用
   *
   * @default 0
   */
  readonly delay?: number;
  /**
   * 下发节流 (与防抖冲突),必须依赖 dp-kit 拦截器才可使用
   *
   * @default 0
   */
  readonly throttle?: number;
  /**
   * 下发防抖 (与节流冲突),必须依赖 dp-kit 拦截器才可使用
   *
   * @default 0
   */
  readonly debounce?: number;
  /**
   * DP 解析与反解析协议,必须依赖 dp-kit 拦截器才可使用
   */
  readonly protocols?: Partial<Record<keyof DpState, any>>;
};
 
export interface IgnoreDpChangeInterceptorOptions {
  /**
   * 白名单 dp,不会忽略上报
   */
  whiteDpCodes?: string[];
  /**
   * 调试日志
   */
  debug?: boolean;
}
 
export interface DevPropInterceptorOptions {
  /**
   * 黑名单 dp,不会同步到云端
   */
  blackDpCodes?: string[];
  /**
   * 首次进入时,默认的设备状态
   */
  defaultState?: Record<string, any>;
}

例如,如果期望应用内的下发都具有 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 的回调值,方便业务判断或区分实际功能点上报自行处理,原始 onDpDataChange 事件则不会被触发。

此外,如果您想针对单个 dp 点下发不再等到 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 功能点上报时进行响应,其他功能点上报会忽略
    },
  },
});

当忽略设备上报响应时,退出面板重新进入后,设备上一次的状态会无法获取,您可以使用 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 功能点不会做云端数据同步
    }
  },
});

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

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

(可选)onBeforeSendDp / onAfterSendDp

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

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

进阶阅读

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

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

教程地址

Beacon 设备面板模板

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

教程地址

👉 立即免费领取开发资源,体验涂鸦 MiniApp 小程序开发。  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IoT砖家涂拉拉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值