npm package: @ray-js/tuya-dp-kit
Description: Tuya Dp Kit from dp send and response
Latest version: 0.0.9

# Tuya Dp Kit

轻量的面板 dp 处理工具库，加入 Redux 数据流，内置 [tuya-dp-transform](https://www.npmjs.com/package/@ray-js/tuya-dp-transform)。

支持 RN / Ray 下使用。

## 安装

```bash
yarn add @ray-js/tuya-dp-kit
```

## 用法

### `createDpKitMiddleware`

配置化的创建 dp 处理中间件的函数

```ts

const createDpKitMiddleware: <D extends Record<string, any>>({
  rawDpMap?: Partial<Record<keyof D, DpMap>>;
  sendDpOption?: SendDpOption<D>;
} = {}) => Middleware;
```

#### 参数

#### `putDeviceData` (必填)

实际的下发方法，若用于 RN 面板开发，通常为`TYSdk.device.putDeviceData`

#### `rawDpMap` (可选)

指定应用里的协议型 dp 的解析方式(参考[tuya-dp-transform](https://www.npmjs.com/package/@ray-js/tuya-dp-transform))，将自动解析&反解析这类 dp，无需再在业务代码里进行处理。

```ts
const dpKitMiddleware: Middleware = createDpKitMiddleware<DpState>({
  rawDpMap: {
    colour_data: [
      {
        name: 'hue',
        bytes: 2,
        extension: {
          min: 0,
          max: 360,
        },
      },
      {
        name: 'saturation',
        bytes: 2,
        extension: {
          min: 0,
          max: 1000,
        },
      },
      {
        name: 'brightness',
        bytes: 2,
        extension: {
          min: 0,
          max: 1000,
        },
      },
    ],

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

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

/**
 * action(RESPONSE_UPDATE_DP): colour_data => 000003e803e8
 * state.dpState.colour_data => { hue: 0, saturation: 1000, brightness: 1000 }
 */

/**
 * action(CHANGE_DP): colour_data => { hue: 0, saturation: 1000, brightness: 1000 }
 * 下发: colour_data => 000003e803e8
 * /
```

#### `sendDpOption` (可选)

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

```ts
type SendDpOption<D = any> = {
  /**
   * 立即触发state更新
   */
  immediate?: boolean;
  /**
   * 多个dp是否按对象里的顺序下发(可以确保固件收到的顺序)
   */
  ordered?: boolean;
  /**
   * 重复值不下发
   *
   * 与当前`dpState`进行比较，重复值检出不下发
   */
  checkRepeat?: boolean;
  /**
   * 过滤过时的下发引起的上报
   *
   * 比如：Slider通常同时受控于用户滑动、点击以及dp上报值，这就可能造成：短时间内连续滑动、点击触发多次下发，后续接收到多次上报后Slider会出现抖动。
   * 建议和`immediate`一起使用
   */
  filterExpired?: boolean;
  /**
   * 延迟下发
   */
  delay?: number;
  /**
   * 下发节流
   */
  throttle?: number;
  /**
   * 下发防抖
   */
  debounce?: number;
  /**
   * dp解析与反解析
   */
  rawDpMap?: Partial<Record<keyof D, DpMap>>;
};
```

例如，如果期望应用内的下发都具有 600ms 的节流，你可以：

```ts
const dpKitMiddleware: Middleware = createDpKitMiddleware<DpState>({
  sendDpOption: {
    throttle: 600,
  },
});
```

或者，如果期望`state.dpState`不再等到 dp 上报后才更新，你可以：

```ts
const dpKitMiddleware: Middleware = createDpKitMiddleware<DpState>({
  sendDpOption: {
    immediate: true,
  },
});
```

#### `onBeforeSendDp` / `onAfterSendDp` (可选)

下发 dp 前/后的钩子（在 putDeviceData 之前/之后），建议传入 RootState 以确保获得正确的 type

```ts
const dpKitMiddleware: Middleware = createDpKitMiddleware<DpState, RootState>({
  // dpState @ 当次下发的dp
  onBeforeSendDp: (dpState, { getState, dispatch }) => {},
  onAfterSendDp: (dpState, { getState, dispatch }) => {},
});
```

#### onResponseDp

监听到 dp 上报后的钩子（在 store 状态变化之前），建议传入 RootState 以确保获得正确的 type

```ts
const dpKitMiddleware: Middleware = createDpKitMiddleware<DpState, RootState>({
  // dpState @ 当次上报的dp
  onResponseDp: (dpState, { getState, dispatch }) => {},
});
```

#### onInitializeDp

监听到 dp 初始化的钩子（由 DevInfoChange 引起，在 store 状态变化之前），建议传入 RootState 以确保获得正确的 type

```ts
const dpKitMiddleware: Middleware = createDpKitMiddleware<DpState, RootState>({
  // dpState @ 初始化的dp
  onInitialize: (dpState, { getState, dispatch }) => {},
});
```

### `updateDpCreator`

`updateDpCreator` 创建一个用于下发 dp 的 actionCreator。若需要 `tuya-dp-kit` 提供的能力，请确保应用内的下发统一使用 `dispatch(updateDp({...})`的方式，不再使用原生的 `TYSdk.device.putDeviceData({...})`。

你可以通过`updateDp({}, options)`使用一些配置式的选项来增强你这一次的下发功能。

最初，你需要调用`updateDpCreator`来创建一个 updateDp 函数，例如：

```ts
export const updateDp = updateDpCreator<Partial<DpState>>();
```

后续你就可以通过这个函数来下发你的 dp 并开启一些你需要的特性了。

例如，如果期望针对开关有 1s 的节流，你可以：

```ts
dispatch(updateDp({ switch: true }, { throttle: 1000 }));
```

或者，如果期望亮度(很可能是一个 Slider 组件)具备[过滤过时下发引起的上报]的能力，你可以：

```ts
dispatch(updateDp({ brightValue: 60 }, { filterExpired: true }));
```

## 如何在现有项目中使用?

家电 RN 模板已支持 tuya-dp-kit，欢迎体验使用 👏。

如果你需要在公版 RN 面板模板内使用 `tuya-dp-kit`，需要进行一点点的改造。

原有的`updateDp`下发 dp 的逻辑是借助`redux-observable`(`rx` 的一个中间件库)来实现的，但仅仅想要实现“`updateDp`下发 dp” 完全不需要用到`rx`，大材小用且让我们的工程变得臃肿(ios bundle 体积增加了约 190kb)。

因此，引入`tuya-dp-kit`后，你可以把工程内`redux-observable`的逻辑全部移除。

如果不太清楚相关的逻辑在哪，你可以在`src/models`下搜索`epic`，删掉所有相关代码即可。

同时，相关的库也可以一并移除

```bash
yarn remove redux-observable rxjs rxjs-compat
```

这样以后，就可以使用了`tuya-dp-kit`了，只需要：

- 通过`createDpKitMiddleware`创建一个中间件并加入已有的中间件

```ts
// src/models/configureStore.ts
const dpKitMiddleware = createDpKitMiddleware<DpState>({
  putDeviceData: someSendDpFunction /* 下发dp的方法 */,
});

const middlewares = isDebuggingInChrome
  ? [logger, dpKitMiddleware]
  : [dpKitMiddleware];
```

- 创建一个`updateDp`函数

```ts
// src/models/modules/common.ts
export const updateDp = updateDpCreator<Partial<DpState>>();
```

## FAQ

1. 为什么初始的 raw dp 转换不起作用?

注意一下，初始化的 devInfoChange 的 action type 是否为`DEV_INFO_CHANGE`，部分模板里可能为`_DEVINFOCHANGE_`，需要纠正

附上目前起作用的几个 action

```typescript
export const CHANGE_DP = 'CHANGE_DP';
export const RESPONSE_UPDATE_DP = 'RESPONSE_UPDATE_DP';
export const DEV_INFO_CHANGE = 'DEV_INFO_CHANGE';
```

非常简单，开始使用吧！