本文档面向已经了解 面板小程序开发 的开发者,你需要充分的了解什么是面板小程序 产品功能 若您对上述概念有所疑问,我们推荐你去看一些预备知识。如果您已了解,可跳过本文。

理解关系

面板作为 IoT 智能设备在 App 终端上的产品形态,创建产品之前,首先来了解一下什么是面板,以及和产品、设备之间的关系。

  1. 面板 是运行在 智能生活 App、OEM App(涂鸦定制 App) 上的界面交互程序,用于控制 智能设备 的运行,展示 智能设备 实时状态。
  2. 产品 将 面板 与 智能设备 联系起来,产品描述了其具备的功能、在 App 上面板显示的名称、智能设备拥有的功能点等。
  3. 智能设备 是搭载了 涂鸦智能模组 的设备,通常在设备上都会贴有一张二维码,使用 智能生活 App 扫描二维码,即可在 App 中获取并安装该设备的控制 面板。
  4. 产品、面板、设备 之间的关系可参考下图。

电工模板使用 SDM(Smart Device Model) 开发,关于 SDM 相关可以 查看 SDM 文档

相关概念

产品名称:通用电工插座

产品介绍

提供插座的打开与关闭的基本功能,并提供关闭倒计时、增加电量、更新当前电流、功率参数等功能,根据以下通用电工的开发教程帮助您开发面板小程序去控制插座。

需求原型

  1. 首页 点击中间按钮切换 switch_1 开关状态。
  2. 首页 页面展示倒计时提示,根据开关状态、手机系统的 24 小时制或 12 小时制、countdown_1 的值来显示对应的提示文案,若未设置倒计时则不展示。
  3. 点击首页底部倒计时 弹出倒计时提示框组件来设置倒计时,若未设置则展示设置倒计时的组件,若已设置则可关闭倒计时。
  4. 点击首页底部设置进入设置页面,可以对除 switch_1、countdown_1 的所有 可下发可上报(rw)功能进行设置。

功能汇总

当前电工插座模板必须的功能点:

switch_1,
countdown_1,

开关

参数

取值

dpid

1

code

switch_1

type

布尔型(Bool)

mode

可下发可上报(rw)

开关 1 倒计时

参数

取值

dpid

9

code

countdown_1

type

数值型(Value)

mode

可下发可上报(rw)

property

数值范围: 0-86400, 间距: 1, 倍数: 0, 单位: s

上面为首页需求必须的功能点,设置页功能点这里不做介绍。

首先需要创建一个电工类产品,定义产品有哪些功能点,然后面板中再根据这些功能点一一实现。

进入IoT 平台,点击左侧产品菜单,产品开发,创建产品,选择标准类目 -> 电工 -> 插座:

选择功能点,这里我们只需要默认的标准功能即可。

🎉 在这一步,我们创建了一个名为 PublicSocket的电工插座产品。

开发者平台创建面板小程序

这部分我们在 小程序开发者 平台上进行操作,注册登录 小程序开发者平台。

创建 IDE 项目

打开 IDE,选择新建,输入项目名称,关联创建的面板小程序,选择单插产品,然后点击下一步。

在下一步中,找到电工单插模板,并完成创建项目

此时,回到项目管理列表,选择刚创建好的项目,进入后,即可进行预览并开发。

工程目录

上面的步骤我们已经初始化好了一个面板小程序的开发模板,下面我们介绍下工程目录。

├── src
│ ├── app.config.ts # 自动生成配置
│ ├── app.tsx # App 根组件
│ ├── components # 组件目录
│ ├── constant # 常量目录
│ ├── devices # 智能设备模型目录
│ │ ├── index.ts # 定义并导出智能设备模型
│ │ └── schema.ts # 当前智能设备 DP 功能点描述,IDE 可自动生成
│ ├── global.config.ts
│ ├── hooks # 自定义 hooks 目录
│ ├── i18n # 多语言目录
│ ├── pages # 页面目录
│ └── routes.config.ts # 路由配置
│─── typings #业务类型定义目录
│ └── sdm.d.ts #智能设备类型定义文件

需求实现

1. IDE 生成 SDM schema到项目。

生成 SDM schema 至项目中,可以查看 src/devices/schema.ts

export const defaultSchema = [
  {
    attr: 128,
    canTrigger: true,
    code: 'switch_1',
    defaultRecommend: false,
    editPermission: false,
    executable: true,
    extContent: '',
    iconname: 'icon-dp_power2',
    id: 1,
    mode: 'rw',
    name: '开关1',
    property: {
      type: 'bool',
    },
    type: 'obj',
  },
  {
    attr: 640,
    canTrigger: true,
    code: 'countdown_1',
    defaultRecommend: true,
    editPermission: false,
    executable: true,
    extContent: '',
    iconname: 'icon-dp_time2',
    id: 9,
    mode: 'rw',
    name: '开关1倒计时',
    property: {
      unit: 's',
      min: 0,
      max: 86400,
      scale: 0,
      step: 1,
      type: 'value',
    },
    type: 'obj',
  },
  // ...
];

2. 需求 -首页 点击中间按钮切换 switch_1 开关状态

分析需求:

  1. 首页添加组件 开关按钮 。
    1. 根据 switch_1 下发的值展示不同按钮图片。
    2. 点击开关组件触发事件,上报「开」「关」的 dp 值。
  2. 确认开关 dp 为switch_1,可作为属性传入组件。
  3. 使用 useProps 获取实时下发的 dp 值。示例:
    import React from 'react';
    import { View } from '@ray-js/ray';
    import { useProps } from '@ray-js/sdm-react';
    
    export default function () {
      const switch_1 = useProps((dpState) => dpState.switch_1);
      return (
        <View style={{ flex: 1 }}>
          <View>switch_1: {switch_1}</View>
        </View>
      );
    }
    
  4. 使用 useActions 获取dp上报的行为示例:
    import React from 'react';
    import { View } from '@ray-js/ray';
    import { useActions } from '@ray-js/sdm-react';
    
    export default function () {
      const actions = useActions();
      return (
        <View style={{ flex: 1 }}>
          <Button
            onClick={() => {
              actions.switch_1.set(false);
            }}
          >
            change action
          </Button>
        </View>
      );
    }
    

根据上述分析,我们来实现开关组件。

import React from 'react';
import clsx from 'clsx';
import { View } from '@ray-js/ray';
import { DpBooleanAction } from '@tuya-miniapp/sdm';
import { useProps, useActions } from '@ray-js/sdm-react';
import { Icon } from '@/components/icon';
import { icons } from '@/res';
import styles from './index.module.less';

export interface Props {
  dpCode: string;
}

export const PowerButton = React.memo<Props>(props => {
  const { dpCode } = props;
  const value = useProps(dpState => dpState[dpCode]);

  const actions = useActions();

  if (!dpCode || typeof value !== 'boolean') {
    return null;
  }

  const action = actions[dpCode] as DpBooleanAction;

  return (
    <View className={styles['power-button']} onClick={action.toggle}>
      <View
        className={clsx(styles['power-button-content'], {
          [`${styles['power-button-off']}`]: !value,
        })}
      />
      <Icon
        className={styles['power-button-icon']}
        d={icons.power}
        size="34px"
        fill={value ? '#ef550d' : '#ffffff'}
      />
    </View>
  );
});

开关组件实现

我们实现了 PowerButton 组件,下面的代码就可以在首页引入,并传入开关的 dp code,下面修改首页的代码。

import React from 'react';
import { setNavigationBarTitle, View } from '@ray-js/ray';
import { useDevice } from '@ray-js/sdm-react';
import { PowerButton } from '@/components';
import styles from './index.module.less';

export default function Home() {
  const deviceName = useDevice((d) => d.devInfo.name);

  React.useEffect(() => {
    setNavigationBarTitle({ title: deviceName });
  }, [deviceName]);

  return (
    <View className={styles.view}>
      <View className={styles.content}>
        <View className={styles['space-between']}>
          <PowerButton dpCode="switch_1" />
        </View>
      </View>
    </View>
  );
}

引入了代码之后,我们的首页有了一个可操控的按钮,这时我们可以修改 IDE右侧控制面板 -> 1.开关 -> false/true 进行虚拟面板dp点的下发与上报,也可以点击我们刚刚完成的组件,查看IDE 控制面板的状态来验证我们的产品功能。

1. 首页 页面展示倒计时提示,根据开关状态、手机系统的 24 小时制或 12 小时制、countdown_1 的值来显示对应的提示文案,若未设置倒计时则不展示。

  1. 创建倒计时提示组件,根据 开关状态 switch_1 及倒计时 countdown_1 状态下发的值来控制展示内容。

倒计时提示组件实现

2. 点击首页底部倒计时 弹出倒计时提示来设置倒计时,若未设置则展示设置倒计时的组件,若已设置则可关闭倒计时。

  1. 创建底部 Tab 组件及倒计时弹窗组件,根据 倒计时 countdown_1 状态下发的值来控制展示内容。
  2. 通过 useActions 上报 倒计时 countdown_1的状态。

倒计时弹窗组件实现

3. 点击首页底部设置进入设置页面,可以对除 switch_1、countdown_1 的所有 可下发可上报(rw)功能进行设置。

  1. 设置页面展示所有 可下发可上报(rw)的功能并进行设置。
  2. 通过 useDevice 获取全部 功能点 dp
    import { useDevice } from '@ray-js/sdm-react';
    const { devInfo } = useDevice();
    // 获取到全部 dp schema
    const dpSchemas = devInfo.schema;
    
  3. 根据不同功能点 dp的类型进行展示不同 UI 及交互弹窗。

设置页面实现