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 小程序开发。