本文档面向已经了解 面板小程序开发 的开发者,您需要充分了解什么是 面板小程序 和 产品功能。若对上述概念有所疑问,涂鸦推荐您了解以下预备知识。如已了解,可跳过本文。
面板是 IoT 智能设备在 App 终端上的产品形态。创建产品之前,首先来了解一下什么是面板,以及面板和产品、设备之间的关系:

照明灯串模板使用 SDM (Smart Device Model) 开发。关于 SDM 相关,可以参考 SDM 文档。
产品名称:照明幻彩灯串
照明灯串是一种支持多路控制的智能照明设备,根据支持的功能点不同,可分为不同路数:
colour_data、白光亮度 bright_value、白光色温 temp_value本模板主要支持 五路 灯串(彩光模式),其他路数可按逻辑自行适配。
switch_led DP 切换开关状态。paint_colour_1 DP 下发数据bright_value)temp_value,如果产品支持)rgbic_linerlight_scene DP 下发dreamlightmic_music_data 持续下发diy_scene DP 下发数据led_number_set)和段数设置(segment_num_set)当前照明灯串模板必需的功能点:
switch_led,
work_mode,
paint_colour_1,
rgbic_linerlight_scene,
dreamlightmic_music_data,
music_data,
led_number_set,
segment_num_set,
当前照明灯串模板可选的功能点:
countdown,
bright_value,
temp_value,
colour_data,
diy_scene,
参数 | 取值 |
dpid | 20 |
code | switch_led |
type | 布尔型(Bool) |
mode | 可下发可上报(rw) |
参数 | 取值 |
dpid | 21 |
code | work_mode |
type | 枚举型(Enum) |
mode | 可下发可上报(rw) |
property | 枚举值: white, colour, scene, music |
参数 | 取值 |
dpid | 22 |
code | bright_value |
type | value |
mode | 可下发可上报(rw) |
property | { "min": 10, "max": 1000, "scale": 0, "step": 1, "type": "value" } |
参数 | 取值 |
dpid | 23 |
code | temp_value |
type | value |
mode | 可下发可上报(rw) |
property | { "min": 0, "max": 1000, "scale": 0, "step": 1, "type": "value" } |
参数 | 取值 |
dpid | 24 |
code | colour_data |
type | 字符串型(String) |
mode | 可下发可上报(rw) |
参数 | 取值 |
dpid | 26 |
code | countdown |
type | value |
mode | 可下发可上报(rw) |
property | { "unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1, "type": "value"} |
参数 | 取值 |
dpid | 27 |
code | music_data |
type | 字符串型(String) |
mode | 可下发可上报(rw) |
参数 | 取值 |
dpid | 52 |
code | dreamlightmic_music_data |
type | raw |
mode | 可下发可上报(rw) |
property | { "type": "raw", "maxlen": 128 } |
参数 | 取值 |
dpid | 58 |
code | led_number_set |
type | value |
mode | 可下发可上报(rw) |
property | { "min": 0, "max": 99999, "scale": 0, "step": 1, "type": "value" } |
参数 | 取值 |
dpid | 63 |
code | segment_num_set |
type | value |
mode | 可下发可上报(rw) |
property | { "min": 2, "max": 20, "scale": 0, "step": 1, "type": "value" } |
参数 | 取值 |
dpid | 69 |
code | paint_colour_1 |
type | raw |
mode | 可下发可上报(rw) |
property | { "type": "raw", "maxlen": 128 } |
参数 | 取值 |
dpid | 56 |
code | rgbic_linerlight_scene |
type | raw |
mode | 可下发可上报(rw) |
property | { "type": "raw", "maxlen": 128 } |
参数 | 取值 |
dpid | 74 |
code | diy_scene |
type | raw |
mode | 可下发可上报(rw) |
property | { "type": "raw", "maxlen": 128 } |
以上为 raw 类型 以外的功能点,raw 类型 DP 的具体协议格式请查看代码实现或产品方案定义。
首先需要创建一个产品,定义产品有哪些功能点,然后再在面板中一一实现这些功能点。
注册登录 涂鸦开发者平台,并在平台创建产品:
注意:本模板主要支持幻彩灯串五路,其它路数可按逻辑自行适配。
🎉 在这一步,一个名为 LampString 的照明灯串产品创建完成。
面板小程序的开发在 小程序开发者 平台上进行操作,首先请前往 小程序开发者平台 完成平台的注册登录。
详细操作步骤可以参考 面板小程序 > 创建面板小程序。
面板模板仓库:仓库地址
git clone https://github.com/Tuya-Community/tuya-ray-materials.git
cd ./template/PublicPanelStringLamp
完成以上步骤后,一个面板小程序的开发模板初始化完成。以下为工程目录及其介绍:
├── public
│ ├── images # 项目的静态资源
├── src
│ ├── app.config.ts # 自动生成配置
│ ├── app.tsx # App 根组件
│ ├── components # 组件目录
│ │ ├── home-head # 首页头部组件(包含开关)
│ │ ├── light # 调光组件
│ │ │ ├── colorLight # 彩光调光
│ │ │ ├── whiteLight # 白光调光
│ │ │ └── SmearLight # 涂抹灯串组件
│ │ ├── light-strip # 灯串显示组件
│ │ ├── scene # 情景组件
│ │ ├── music # 音乐组件
│ │ ├── tab-diy # DIY 场景组件
│ │ └── work-mode # 工作模式切换组件
│ ├── constant # 常量目录
│ │ └── dpCodes.ts # DP 代码常量定义
│ ├── containers # 聚合的组件目录
│ ├── devices # 智能设备模型目录
│ │ ├── index.ts # 定义并导出智能设备模型
│ │ ├── protocols # 定义当前设备所需要的 DP 声明
│ │ │ ├── index.ts # 协议导出
│ │ │ ├── paintColour1.ts # paint_colour_1 DP 协议解析
│ │ │ ├── RgbicLinerlightSceneFormater.ts # 情景 DP 协议解析
│ │ │ ├── DiySceneFormatter.ts # DIY 场景 DP 协议解析
│ │ │ └── powerMemoryParser.ts # 断电记忆协议解析
│ │ └── schema.ts # 当前智能设备 DP 功能点描述,IDE 可自动生成
│ ├── global.config.ts
│ ├── hooks # 自定义 Hooks 目录
│ │ ├── useIsSupport.ts # 判断是否支持某个 DP
│ │ ├── useSceneSet.ts # 情景设置相关
│ │ └── ...
│ ├── i18n # 多语言目录
│ ├── pages # 页面目录
│ │ ├── home # 首页
│ │ ├── diyEdit # DIY 编辑页面
│ │ ├── staticDiyEdit # 静态 DIY 编辑页面
│ │ └── ...
│ └── routes.config.ts # 路由配置
├── typings # 业务类型定义目录
│ └── sdm.d.ts # 智能设备类型定义文件
生成 SDM schema 至项目中,可以查看 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,可作为属性传入组件。useProps 获取实时下发的 DP 值。示例: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 下发 DP。关于如何通过 SDM 下发 DP,请参考:SDM 下发教程。示例: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>
);
}
根据上述分析,来实现开关组件: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>
);
};
工作模式切换组件位于 src/components/work-mode/index.tsx,支持在 white、colour、scene、music、diy 之间切换。
// 使用示例
<WorkMode
current={work_mode}
onChange={mode => {
if (['colour', 'white', 'scene', 'music'].includes(mode)) {
deviceActions.work_mode.set(mode);
}
if (mode === 'diy') {
// 初始化 DIY 场景
setWorkModeDiy('diy');
}
setWorkMode(mode);
}}
/>

彩光调节功能主要通过 paint_colour_1 DP 实现,该 DP 为 raw 类型,需要自定义协议解析器。
src/devices/protocols/paintColour1.ts该文件定义了 SmearFormater 类,用于解析和格式化 paint_colour_1 DP 数据。export enum DimmerMode {
white, // 白光模式
colour, // 彩光模式
colourCard, // 色卡模式
combination, // 组合模式
}
export enum SmearMode {
all, // 全选(油漆桶)
single, // 单点涂抹
clear, // 橡皮擦
}
export interface SmearDataType {
version: number; // 版本号
dimmerMode: DimmerMode; // 调光模式
effect?: number; // 涂抹效果(0: 无, 1: 渐变)
ledNumber?: number; // 灯串 UI 段数
smearMode?: SmearMode; // 涂抹动作
hue?: number; // 彩光色相
saturation?: number; // 彩光饱和度
value?: number; // 彩光亮度
brightness?: number; // 白光亮度
temperature?: number; // 白光色温
singleType?: number; // 点选类型(0: 连续,1: 单点)
quantity?: number; // 当次操作的灯串数
indexs?: Set<number>; // 选中的灯珠索引
combination?: ColourData[]; // 颜色组合
}
src/components/light-strip/index.tsx该组件负责渲染灯串 UI,支持:src/components/light/index.tsx该组件整合了灯串显示和调色功能,包括:ColorLight)WhiteLight)SmearLight)import { useActions, useStructuredActions, useStructuredProps } from '@ray-js/panel-sdk';
import { DimmerMode, SmearMode } from '@/devices/protocols/paintColour1';
// 获取当前涂抹数据
const paintColorData = useStructuredProps(p => p.paint_colour_1);
const structuredActions = useStructuredActions();
// 下发全选彩光
const handleSetAllColor = (hue: number, saturation: number, value: number) => {
structuredActions.paint_colour_1.set({
dimmerMode: DimmerMode.colour,
smearMode: SmearMode.all,
hue,
saturation,
value,
ledNumber: 20, // 灯珠数量
});
};
// 下发选中灯珠的颜色
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,
});
};

情景功能通过 rgbic_linerlight_scene DP 实现,该 DP 为 raw 类型。
src/devices/protocols/RgbicLinerlightSceneFormater.tssrc/components/scene/index.tsx支持:// 使用示例
import { useStructuredActions } from '@ray-js/panel-sdk';
const structuredActions = useStructuredActions();
// 设置情景
structuredActions.rgbic_linerlight_scene.set({
key: 1, // 情景 ID
value: {
// 情景数据
colors: [
{ hue: 0, saturation: 1000, value: 1000 },
// ...
],
},
});
音乐律动支持两种模式:

dreamlightmic_music_data DP 下发 src/components/music/local/index.tsx
music_data DP 下发 src/components/music/app/index.tsx// 本地音乐使用示例
import { useActions } from '@ray-js/panel-sdk';
const actions = useActions();
// 设置本地音乐模式
actions.dreamlightmic_music_data.set(musicData, {
success() {
console.log('音乐模式设置成功');
},
});

DIY 场景编辑功能通过 diy_scene DP 实现,支持两种编辑模式:
src/pages/staticDiyEdit/index.tsxsrc/pages/diyEdit/index.tsx// DIY 场景保存示例
import { useStructuredActions } from '@ray-js/panel-sdk';
const structuredActions = useStructuredActions();
// 保存 DIY 场景
structuredActions.diy_scene.set({
name: '我的场景',
segments: [
{
colors: [
{ hue: 0, saturation: 1000, value: 1000 },
// ...
],
},
],
});
首页根据产品支持的功能点显示对应的 Tab 页:
paint_colour_1 DP。paint_colour_1 DP 只能记忆最近一次的灯光数据,需要将灯串的每个灯珠颜色值进行云端存储,每当 DP 变换时进行云端灯珠颜色同步。src/components/scene/scenes.tsdiy_scene DP 下发rgbic_linerlight_scene DP 下发情景数据dreamlightmic_music_data DP 下发dreamlightmic_music_data DP 持续下发src/standModel/musicModel/ 目录下的实现
led_number_set DP 设置segment_num_set DP 设置对于简单协议转换,可以直接在 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,
},
],
};
对于高级协议转换,可将对应协议规则放到 src/devices/protocols/ 目录下,再到 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),
};
使用 useSupport Hook:
import { useSupport } from '@ray-js/panel-sdk';
const support = useSupport();
// 判断是否支持彩光
const isSupportColour = support.isSupportColour();
// 判断是否支持白光
const isSupportBright = support.isSupportBright();
// 判断是否支持某个 DP
const isSupportDp = support.isSupportDp('paint_colour_1');
在 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),
};
参考 src/components/light/index.tsx 中的 handleUpdate2Cloud 方法,使用云端存储能力保存灯珠颜色数据。
参考 src/pages/diyEdit/index.tsx 和 src/pages/staticDiyEdit/index.tsx 的实现,支持两种编辑模式。
本文档介绍了照明灯串模板的核心功能实现,包括:
switch_led DP 实现paint_colour_1 DP 实现涂抹调色功能rgbic_linerlight_scene DP 实现开发者可以根据实际需求,参考本文档和代码实现,进行功能扩展和定制。