---
title: dp-kit
---

# dp-kit

<Alert type="info">
dp-kit added since @ray-js/panel-sdk@1.7.0
</Alert>

A lightweight toolkit for handling device-panel DP features, with built-in parsing for complex DP features, send option enhancements (`sendOptions`), and optimistic UI updates for complex DP scenarios.

<Image src="/images/panel/sdm-interceptor-dp-kit.gif" />

## When do you need dp-kit?

✅ **Good fit**:
- Your product has complex DP features, such as raw/string payloads like `colour_data` that need to be parsed into `hue / saturation / value`
- You need throttling, debouncing, or delayed DP sending
- You want to update the UI immediately after sending (`immediate` mode)
- You need to ignore device report responses and synchronize DP state from the cloud
- You need custom logic before or after sending (`onBeforeSendDp` / `onAfterSendDp`)

❌ **Probably unnecessary**:
- Your product only uses simple DP feature types (`bool / enum / value`), and `useProps + useActions` is enough
- You do not need any send-time enhancements

## Integration Guide

### Step 1: Configure the product DP schema file

> `src/devices/schema.ts`

See [Creating SDM](/en/miniapp/solution-panel/ability/common/sdm/usage#devicesschemats)

### Step 2: Configure the complex DP parsing file (Optional) 

> `src/devices/protocols.ts`

- This step maps to the `protocols` field in createDpKit and defines how complex DP payloads are parsed and formatted. Skip it if your product does not use complex DP data.

```ts | pure
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: Create dp-kit for your scenario

> `src/devices/index.ts`

```ts | pure
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: Initialize complex DP protocol data (Optional) 

> `src/app.tsx`

- Integrate `dpKit.init` to initialize complex DP protocol data. You can skip this if you do not use structured DP data.

### Step 5: Integrate global type definitions (Optional) 

> `typings/sdm.d.ts`

- Refer to the latest template file:
  [PublicSdmTemplate/typings/sdm.d.ts](https://github.com/Tuya-Community/tuya-ray-materials/blob/main/template/PublicSdmTemplate/typings/sdm.d.ts)
- Replace the template imports such as `@/devices/schema` and `@/devices/protocols` with the actual paths in your project
- Skip this step if you do not need TS IntelliSense

### Step 6: Read and send complex DP protocol data

> `src/pages/home/index.tsx`

- Use `useStructuredProps` to read structured DP data
- Use `useStructuredActions` to send structured DP data

```tsx | pure
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

For the complete `createDpKit` API reference and full type definitions, see [createDpKit API](/en/miniapp/solution-panel/ability/common/sdm/interceptors/dpkit/createDpKit).

The sections below focus on how each option is used in practice.

### Complex DP parsing (Optional) 

This section maps to the `protocols` field in createDpKit. Use it to describe how complex DP features should be parsed and formatted, so your business code no longer needs to deal with raw payload conversions.

```ts | pure
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) => {
        // Custom parsing
      },

      formatter: (dpValue) => {
        // Custom formatting
      },
    },
  },
});

/**
 * Read colour_data
 * useProps(props => props.colour_data) = 000003e803e8
 * useStructuredProps(props => props.colour_data) = { hue: 0, saturation: 1000, brightness: 1000 }
 */

/**
 * Send colour_data
 * const actions = useActions(); => actions.colour_data.set('000003e803e8');
 * const actions = useStructuredActions(); => actions.colour_data.set({ hue: 0, saturation: 1000, brightness: 1000 });
 */
```

If the payload shape is completely fixed, you can still use a shorter mapping-style config, but `parser / formatter` is usually easier to understand in tutorials.

### Send options (`sendOptions`) (Optional) 

These are the options used when sending DP features. In `createDpKit`, they correspond to the `sendDpOption` field and become the default behavior for later send calls.

> For the full type definition, see [createDpKit API > SendDpOption](/en/miniapp/solution-panel/ability/common/sdm/interceptors/dpkit/createDpKit#SendDpOption)

| Property | Type | Default | Description |
|-----|------|-------|------|
| `immediate` | `boolean` | `false` | Update the UI immediately after sending, without waiting for a device report |
| `throttle` | `number` | `0` | Send throttling in ms |
| `debounce` | `number` | `0` | Send debouncing in ms; conflicts with throttle |
| `delay` | `number` | `0` | Delay sending in ms |
| `checkRepeat` | `boolean` | `false` | Skip repeated values |
| `ordered` | `boolean` | `false` | Send multiple DP features in order |
| `ignoreDpDataResponse` | `boolean \| IgnoreDpChangeInterceptorOptions` | `false` | Ignore device report responses (1.11.0+) |
| `synchronizeDevProperty` | `boolean \| DevPropInterceptorOptions` | `false` | Synchronize DP state with cloud device properties (1.11.0+) |
| `protocols` | `Record<string, CustomRawDpMap>` | - | Temporary protocol conversion for a single send |

**`ignoreDpDataResponse` object fields**:

| Property | Type | Default | Description |
|-----|------|-------|------|
| `whiteDpCodes` | `string[]` | - | Whitelisted DP features that should still respond |
| `timeout` | `number` | `10000` | Debounce timeout in ms; `dpDataChange` is triggered only after timeout (1.14.0+) |
| `customRule` | `function` | - | Custom ignore rule with the highest priority (1.14.0+) |
| `debug` | `boolean` | - | Enable debug logs |

**`synchronizeDevProperty` object fields**:

| Property | Type | Description |
|-----|------|------|
| `blackDpCodes` | `string[]` | Blacklisted DP features that should not be synchronized to the cloud |
| `defaultState` | `object \| function` | Default value when cloud data is empty; async is supported |

For example, if every send in your application should use a 600 ms throttle:

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

Or, if you want a DP feature to update immediately without waiting for a device report:

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

With `immediate`, the DP feature data injected by dp-kit will carry an extra `__from__: dp-kit` marker in SDM's internal `onDataChange` flow. This helps distinguish optimistic updates from real device reports. The original [onDpDataChange](/en/miniapp/solution-panel/ability/common/sdm/api/onDpDataChange) event is not triggered.

If you only want to override a single send without waiting for a report:

```ts | pure
const actions = useActions();
actions.power.toggle({ immediate: true });
```

Using only `immediate` still triggers one more update when the device report arrives, which can cause UI flicker. If you want to ignore the device report response entirely:

```ts | pure
const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: true,
  },
});
```

If some DP features should still respond, use a whitelist:

```ts | pure
const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      whiteDpCodes: ['switch'],
    },
  },
});
```

You can also configure a timeout:

```ts | pure
const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      timeout: 1000 * 5,
    },
  },
});
```

Or define a custom rule with the highest priority:

```ts | pure
const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      customRule: (dpState, lastSendTimestamp) => {
        if (dpState.switch !== undefined) {
          return true;
        }
        if (lastSendTimestamp === null) {
          return true;
        }
        const timeDiff = Date.now() - lastSendTimestamp;
        return timeDiff > 8000;
      },
    },
  },
});
```

When device report responses are ignored, the previous state will not be available after exiting and re-entering the panel. Use `synchronizeDevProperty` to restore DP state from the cloud:

```ts | pure
const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    immediate: true,
    ignoreDpDataResponse: {
      whiteDpCodes: ['switch'],
    },
    synchronizeDevProperty: true,
  },
});
```

Every send writes the DP state into cloud device properties. When the panel opens again, dp-kit performs one initialization pass and restores the DP state from the cloud.

You can also exclude specific DP features from synchronization:

```ts | pure
const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    synchronizeDevProperty: {
      blackDpCodes: ['switch'],
    },
  },
});
```

When cloud data is empty, you can define a default state:

```ts | pure
const dpKit = createDpKit<SmartDeviceSchema>({
  sendDpOption: {
    ignoreDpDataResponse: true,
    synchronizeDevProperty: {
      defaultState: {
        switch: false,
      },
    },
  },
});
```

### onBeforeSendDp / onAfterSendDp (Optional) 

Hooks that run before and after `publishDps`. Passing `SmartDeviceSchema` is recommended so the hook receives the correct type.

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

## Further Reading

### logger interceptor

> dp-kit ships well with the built-in logger interceptor for inspecting DP sends and reports in the console.

[logger docs](/en/miniapp/solution-panel/ability/common/sdm/interceptors/logger)

### matter-kit interceptor

> If you also need to handle Matter devices, pair dp-kit with matter-kit.

[matter-kit docs](/en/miniapp/solution-panel/ability/common/sdm/interceptors/matterkit/usage)

### SmartDeviceModel guide

> dp-kit is built on top of the SmartDeviceModel interceptor mechanism.

[SmartDeviceModel docs](/en/miniapp/solution-panel/ability/common/sdm/usage)

### Using SDM interceptors and custom DP protocol parsing

> A tutorial showing custom interceptors and custom protocol parsing built on top of dp-kit.

[Tutorial](https://developer.tuya.com/cn/miniapp-codelabs/codelabs/sdm-transformer/index.html#1)

### Beacon panel template

> A tutorial that builds a Beacon panel with dp-kit built-in capabilities.

[Tutorial](https://developer.tuya.com/cn/miniapp-codelabs/codelabs/panel-sdm-dpkit-beacon/index.html#1)
