This document is intended for developers who are already familiar with Panel Mini Program development. You need to fully understand what Panel Mini Program and Product Features are. If you have questions about these concepts, Tuya recommends that you learn the following prerequisites. If you already understand them, you can skip this section.
A panel is the product form of IoT smart devices in App terminals. Before creating a product, let's first understand what a panel is and the relationships between panels, products, and devices:

The string light template is developed using SDM (Smart Device Model). For information about SDM, refer to the SDM Documentation.
Product Name: Dreamcolor String Light
String lights are smart lighting devices that support multi-channel control. They can be divided into different channels based on the supported function points:
colour_data, white light brightness bright_value, and white light color temperature temp_valueThis template mainly supports three-channel string lights (color light mode). Other channels can be adapted according to the logic.
switch_led DP.paint_colour_1 DPbright_value)temp_value, if the product supports it)rgbic_linerlight_scene DPdreamlightmic_music_datadiy_scene DPled_number_set) and segment count setting (segment_num_set)Required function points for the current string light template:
switch_led,
work_mode,
paint_colour_1,
rgbic_linerlight_scene,
dreamlightmic_music_data,
music_data,
led_number_set,
segment_num_set,
Optional function points for the current string light template:
countdown,
bright_value,
temp_value,
colour_data,
diy_scene,
Parameter | Value |
dpid | 20 |
code | switch_led |
type | Boolean (Bool) |
mode | Readable and writable (rw) |
Parameter | Value |
dpid | 21 |
code | work_mode |
type | Enum |
mode | Readable and writable (rw) |
property | Enum values: white, colour, scene, music |
Parameter | Value |
dpid | 22 |
code | bright_value |
type | value |
mode | Readable and writable (rw) |
property | { "min": 10, "max": 1000, "scale": 0, "step": 1, "type": "value" } |
Parameter | Value |
dpid | 23 |
code | temp_value |
type | value |
mode | Readable and writable (rw) |
property | { "min": 0, "max": 1000, "scale": 0, "step": 1, "type": "value" } |
Parameter | Value |
dpid | 24 |
code | colour_data |
type | String |
mode | Readable and writable (rw) |
Parameter | Value |
dpid | 26 |
code | countdown |
type | value |
mode | Readable and writable (rw) |
property | { "unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1, "type": "value"} |
Parameter | Value |
dpid | 27 |
code | music_data |
type | String |
mode | Readable and writable (rw) |
Parameter | Value |
dpid | 52 |
code | dreamlightmic_music_data |
type | raw |
mode | Readable and writable (rw) |
property | { "type": "raw", "maxlen": 128 } |
Parameter | Value |
dpid | 58 |
code | led_number_set |
type | value |
mode | Readable and writable (rw) |
property | { "min": 0, "max": 99999, "scale": 0, "step": 1, "type": "value" } |
Parameter | Value |
dpid | 63 |
code | segment_num_set |
type | value |
mode | Readable and writable (rw) |
property | { "min": 2, "max": 20, "scale": 0, "step": 1, "type": "value" } |
Parameter | Value |
dpid | 69 |
code | paint_colour_1 |
type | raw |
mode | Readable and writable (rw) |
property | { "type": "raw", "maxlen": 128 } |
Parameter | Value |
dpid | 56 |
code | rgbic_linerlight_scene |
type | raw |
mode | Readable and writable (rw) |
property | { "type": "raw", "maxlen": 128 } |
Parameter | Value |
dpid | 74 |
code | diy_scene |
type | raw |
mode | Readable and writable (rw) |
property | { "type": "raw", "maxlen": 128 } |
The above are function points other than raw type DPs. For the specific protocol format of raw type DPs, please refer to the code implementation or product solution definition.
First, you need to create a product and define what function points the product has, and then implement these function points one by one in the panel.
Register and log in to the Tuya Developer Platform, and create a product on the platform:
Note: This template mainly supports dreamcolor string light three-channel. Other channels can be adapted according to the logic.
๐ At this step, a string light product named LampString has been created.
Panel mini program development is performed on the Mini Program Developer platform. First, go to the Mini Program Developer Platform to complete platform registration and login.
For detailed steps, refer to Panel Mini Program > Create Panel Mini Program.
Panel template repository: Repository Address
git clone https://github.com/Tuya-Community/tuya-ray-materials.git
cd ./template/PublicPanelStringLamp
After completing the above steps, a panel mini program development template is initialized. The following is the project directory and its description:
โโโ public
โ โโโ images # Static resources of the project
โโโ src
โ โโโ app.config.ts # Auto-generated configuration
โ โโโ app.tsx # App root component
โ โโโ components # Component directory
โ โ โโโ home-head # Home page header component (includes switch)
โ โ โโโ light # Dimming component
โ โ โ โโโ colorLight # Color light dimming
โ โ โ โโโ whiteLight # White light dimming
โ โ โ โโโ SmearLight # String light smearing component
โ โ โโโ light-string # String light display component
โ โ โโโ scene # Scene component
โ โ โโโ music # Music component
โ โ โโโ tab-diy # DIY scene component
โ โ โโโ work-mode # Work mode switching component
โ โโโ constant # Constant directory
โ โ โโโ dpCodes.ts # DP code constant definitions
โ โโโ containers # Aggregated component directory
โ โโโ devices # Smart device model directory
โ โ โโโ index.ts # Define and export smart device model
โ โ โโโ protocols # Define DP declarations required by the current device
โ โ โ โโโ index.ts # Protocol export
โ โ โ โโโ paintColour1.ts # paint_colour_1 DP protocol parsing
โ โ โ โโโ RgbicLinerlightSceneFormater.ts # Scene DP protocol parsing
โ โ โ โโโ DiySceneFormatter.ts # DIY scene DP protocol parsing
โ โ โ โโโ powerMemoryParser.ts # Power-off memory protocol parsing
โ โ โโโ schema.ts # Current smart device DP function point description, can be auto-generated by IDE
โ โโโ global.config.ts
โ โโโ hooks # Custom Hooks directory
โ โ โโโ useIsSupport.ts # Check if a DP is supported
โ โ โโโ useSceneSet.ts # Scene setting related
โ โ โโโ ...
โ โโโ i18n # Multi-language directory
โ โโโ pages # Page directory
โ โ โโโ home # Home page
โ โ โโโ diyEdit # DIY editing page
โ โ โโโ staticDiyEdit # Static DIY editing page
โ โ โโโ ...
โ โโโ routes.config.ts # Route configuration
โโโ typings # Business type definition directory
โ โโโ sdm.d.ts # Smart device type definition file
Generate SDM schema to the project, you can view src/devices/schema.ts.
export const defaultSchema = [
{
code: 'switch_led',
defaultValue: '',
canTrigger: true,
iconname: 'icon-dp_power2',
type: 'obj',
executable: true,
mode: 'rw',
defaultRecommend: false,
name: 'ๅผๅ
ณ',
property: {
type: 'bool',
},
subType: 'bool',
id: 20,
editPermission: false,
},
{
code: 'work_mode',
defaultValue: '',
canTrigger: true,
iconname: 'icon-dp_mode',
type: 'obj',
executable: true,
mode: 'rw',
defaultRecommend: false,
name: 'ๅทฅไฝๆจกๅผ',
property: {
range: ['white', 'colour', 'scene', 'music'],
type: 'enum',
},
subType: 'enum',
id: 21,
editPermission: false,
},
// ...
] as const;

switch_led.switch_led, which can be passed as a property to the component.useProps to get the real-time DP value sent.Example:import React from "react";
import { View } from "@ray-js/ray";
import { useProps } from "@ray-js/panel-sdk";
export default function () {
const power = useProps((props) => props.switch_led);
return (
<View style={{ flex: 1 }}>
<View>switch_led: {power}</View>
</View>
);
}
useActions to send DPs. For how to send DPs through SDM, refer to: SDM Sending Tutorial.Example:import React from "react";
import { View } from "@ray-js/ray";
import { useActions } from "@ray-js/panel-sdk";
export default function () {
const actions = useActions();
const handleTogglePower = () => {
actions.switch_led.toggle({ throttle: 300 });
};
return (
<View style={{ flex: 1 }}>
<Button img={power} onClick={handleTogglePower} />
</View>
);
}
Based on the above analysis, implement the switch component: src/components/home-head/index.tsx
import React from 'react';
import { Image, View } from '@ray-js/ray';
import { useActions, useProps } from '@ray-js/panel-sdk';
import Res from '../../res';
import styles from './index.module.less';
export const HomeHead = () => {
const switchLed = useProps(state => state?.switch_led);
const actions = useActions();
return (
<View className={styles.contain}>
<Image
className={styles.head}
mode="aspectFill"
src={switchLed ? Res.homeahead_on : Res.homeahead_off}
/>
<View className={styles.bottom}>
<View
className={`${styles.switch} ${switchLed ? '' : styles.switchOff}`}
hoverClassName="button-hover"
onClick={() => {
actions.switch_led.toggle();
}}
>
<Image mode="aspectFit" className={styles.switchIcon} src={Res.switch_icon} />
</View>
</View>
</View>
);
};
The work mode switching component is located at src/components/work-mode/index.tsx and supports switching between white, colour, scene, music, and diy.
// Usage example
<WorkMode
current={work_mode}
onChange={mode => {
if (['colour', 'white', 'scene', 'music'].includes(mode)) {
deviceActions.work_mode.set(mode);
}
if (mode === 'diy') {
// Initialize DIY scene
setWorkModeDiy('diy');
}
setWorkMode(mode);
}}
/>

The color light adjustment function is mainly implemented through the paint_colour_1 DP, which is a raw type and requires a custom protocol parser.
src/devices/protocols/paintColour1.tsThis file defines the SmearFormater class for parsing and formatting paint_colour_1 DP data.export enum DimmerMode {
white, // White light mode
colour, // Color light mode
colourCard, // Color card mode
combination, // Combination mode
}
export enum SmearMode {
all, // Select all (paint bucket)
single, // Single-point smearing
clear, // Eraser
}
export interface SmearDataType {
version: number; // Version number
dimmerMode: DimmerMode; // Dimming mode
effect?: number; // Smearing effect (0: none, 1: gradient)
ledNumber?: number; // String light UI segment count
smearMode?: SmearMode; // Smearing action
hue?: number; // Color light hue
saturation?: number; // Color light saturation
value?: number; // Color light brightness
brightness?: number; // White light brightness
temperature?: number; // White light color temperature
singleType?: number; // Point selection type (0: continuous, 1: single point)
quantity?: number; // Number of string lights operated this time
indexs?: Set<number>; // Selected LED indices
combination?: ColourData[]; // Color combination
}
src/components/light-string/index.tsxThis component is responsible for rendering the string light UI and supports:src/components/light/index.tsxThis component integrates string light display and color adjustment functions, including:ColorLight)WhiteLight)SmearLight)import { useActions, useStructuredActions, useStructuredProps } from '@ray-js/panel-sdk';
import { DimmerMode, SmearMode } from '@/devices/protocols/paintColour1';
// Get current smearing data
const paintColorData = useStructuredProps(p => p.paint_colour_1);
const structuredActions = useStructuredActions();
// Send select-all color light
const handleSetAllColor = (hue: number, saturation: number, value: number) => {
structuredActions.paint_colour_1.set({
dimmerMode: DimmerMode.colour,
smearMode: SmearMode.all,
hue,
saturation,
value,
ledNumber: 20, // LED count
});
};
// Send color for selected LEDs
const handleSetSelectedColor = (indexs: Set<number>, hsv: { hue: number; saturation: number; value: number }) => {
structuredActions.paint_colour_1.set({
dimmerMode: DimmerMode.colour,
smearMode: SmearMode.single,
hue: hsv.hue,
saturation: hsv.saturation,
value: hsv.value,
indexs,
ledNumber: 20,
});
};

The scene function is implemented through the rgbic_linerlight_scene DP, which is a raw type.
src/devices/protocols/RgbicLinerlightSceneFormater.tssrc/components/scene/index.tsxSupports:// Usage example
import { useStructuredActions } from '@ray-js/panel-sdk';
const structuredActions = useStructuredActions();
// Set scene
structuredActions.rgbic_linerlight_scene.set({
key: 1, // Scene ID
value: {
// Scene data
colors: [
{ hue: 0, saturation: 1000, value: 1000 },
// ...
],
},
});
Music rhythm supports two modes:

dreamlightmic_music_data DP src/components/music/local/index.tsx
music_data DP src/components/music/app/index.tsx// Local music usage example
import { useActions } from '@ray-js/panel-sdk';
const actions = useActions();
// Set local music mode
actions.dreamlightmic_music_data.set(musicData, {
success() {
console.log('Music mode set successfully');
},
});

The DIY scene editing function is implemented through the diy_scene DP and supports two editing modes:
src/pages/staticDiyEdit/index.tsxsrc/pages/diyEdit/index.tsx// DIY scene save example
import { useStructuredActions } from '@ray-js/panel-sdk';
const structuredActions = useStructuredActions();
// Save DIY scene
structuredActions.diy_scene.set({
name: 'My Scene',
segments: [
{
colors: [
{ hue: 0, saturation: 1000, value: 1000 },
// ...
],
},
],
});
The home page displays corresponding Tab pages based on the function points supported by the product:
paint_colour_1 DP.paint_colour_1 DP can only remember the most recent lighting data, it is necessary to store the color value of each LED of the string light in the cloud and synchronize the LED colors in the cloud whenever the DP changes.src/components/scene/scenes.tsdiy_scene DPrgbic_linerlight_scene DPdreamlightmic_music_data DPdreamlightmic_music_data DPsrc/standModel/musicModel/ directory
led_number_set DPsegment_num_set DPFor simple protocol conversion, you can directly declare it in src/devices/protocols/index.ts:
import dpParser from './parsers';
import { lampSchemaMap } from '@/devices/schema';
const { colour_data } = lampSchemaMap;
export const protocols = {
[colour_data.code]: [
{
name: 'hue' as const,
bytes: 2,
default: 0,
defaultValue: 0,
},
{
name: 'saturation' as const,
bytes: 2,
defaultValue: 1,
},
{
name: 'value' as const,
bytes: 2,
defaultValue: 1,
},
],
};
For advanced protocol conversion, you can place the corresponding protocol rules in the src/devices/protocols/ directory, and then reference them in src/devices/protocols/index.ts:
import PaintColour1 from './paintColour1';
import RgbicLinerlightSceneFormater from './RgbicLinerlightSceneFormater';
import DiySceneFormatter from './DiySceneFormatter';
export const protocols = {
[dpCodes.paint_colour_1]: new PaintColour1(),
[dpCodes.rgbic_linerlight_scene]: new RgbicLinerlightSceneFormater(dpCodes.rgbic_linerlight_scene),
[dpCodes.diy_scene]: new DiySceneFormatter(dpCodes.diy_scene),
};
Use the useSupport Hook:
import { useSupport } from '@ray-js/panel-sdk';
const support = useSupport();
// Check if color light is supported
const isSupportColour = support.isSupportColour();
// Check if white light is supported
const isSupportBright = support.isSupportBright();
// Check if a DP is supported
const isSupportDp = support.isSupportDp('paint_colour_1');
Group device handling is already implemented in src/devices/index.ts:
const isGroupDevice = !!getLaunchOptionsSync()?.query?.groupId;
export const devices = {
common: isGroupDevice
? new SmartGroupModel<SmartDeviceSchema, Abilities>(options as any)
: new SmartDeviceModel<SmartDeviceSchema, Abilities>(options),
};
Refer to the handleUpdate2Cloud method in src/components/light/index.tsx, which uses cloud storage capabilities to save LED color data.
Refer to the implementation in src/pages/diyEdit/index.tsx and src/pages/staticDiyEdit/index.tsx, which supports two editing modes.
This document introduces the core function implementation of the string light template, including:
switch_led DPpaint_colour_1 DPrgbic_linerlight_scene DPDevelopers can refer to this document and code implementation to extend and customize functions according to actual needs.