简介
完成基础自定义后,可能需要添加更多功能、修改默认行为、自定义 UI 控制面板等,以满足产品和应用需求。
本节介绍如何自定义设备控制面板界面或参数控制,甚至可以扩展现有的通知功能。
前提条件
假设你已经准备好应用开发环境。请按照说明设置开发环境。以下所有参考内容均基于克隆自 GitHub 的 ESP RainMaker Home 示例应用源码仓库。
假设你已经了解混合应用的工作原理,并熟悉 React Native 和 Expo 应用开发流程。
API 配置
自定义 API 端点
在 rainmaker.config.ts 中自定义 ESP RainMaker API 端点和认证设置:
export const SDKConfig = {
baseUrl: "https://your-custom-api.com", // 自定义 API 端点
version: "v1",
authUrl: "https://your-custom-auth.com", // 自定义 OAuth 端点
clientId: "your-client-id", // OAuth 客户端 ID
redirectUrl: "yourapp://com.yourcompany.yourapp/success", // OAuth 重定向 URL
customStorageAdapter: asyncStorageAdapter,
localDiscoveryAdapter: EspLocalDiscoveryAdapter,
localControlAdapter: ESPLocalControlAdapter,
provisionAdapter: provisionAdapter,
notificationAdapter: ESPNotificationAdapter,
oauthAdapter: espOauthAdapter,
appUtilityAdapter: ESPAppUtilityAdapter,
};
环境特定配置
可以为不同环境创建配置:
// 开发环境配置
const developmentConfig = {
baseUrl: "https://api.staging.rainmaker.espressif.com",
};
// 生产环境配置
const productionConfig = {
baseUrl: "https://api.rainmaker.espressif.com",
};
// 根据环境使用相应的配置
export const SDKConfig = __DEV__ ? developmentConfig : productionConfig;
自定义设备控制面板
打开源代码,在 app/(device)/device_panels/ 目录下创建新的面板组件来自定义设备控制面板。
现有设备面板:
Light.tsx- 灯光设备控制面板Switch.tsx- 开关设备控制面板Temperature.tsx- 温度传感器面板Fallback.tsx- 默认回退面板
创建自定义设备面板
- 在
app/(device)/device_panels/目录下创建新的 TypeScript 文件 - 导入必要的组件和 hooks
- 实现自定义 UI 和控制逻辑
- 在设备配置中注册面板
示例 - 自定义灯光面板结构:
// app/(device)/device_panels/CustomLight.tsx
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import { observer } from "mobx-react-lite";
import { useToast } from "@/hooks/useToast";
import { useTranslation } from "react-i18next";
import {
PowerButton,
BrightnessSlider,
HueSlider,
SaturationSlider,
} from "@/components/ParamControls";
import { ControlPanelProps } from "@/types/global";
const CustomLightPanel: React.FC<ControlPanelProps> = ({ device, node }) => {
const { t } = useTranslation();
const toast = useToast();
return (
<View style={styles.container}>
<PowerButton device={device} node={node} />
<BrightnessSlider device={device} node={node} />
<HueSlider device={device} node={node} />
<SaturationSlider device={device} node={node} />
{/* 在此处添加自定义控件 */}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
});
export default observer(CustomLightPanel);
自定义参数控件
在 components/ParamControls/ 目录下添加新的参数控件类型,以支持自定义设备参数。
现有参数控件:
PowerButton.tsx- 电源开/关控件BrightnessSlider.tsx- 亮度控件HueSlider.tsx- 色相控件SaturationSlider.tsx- 饱和度控件ColorTemperatureSlider.tsx- 色温控件SpeedSlider.tsx- 风扇速度控件TemperatureSlider.tsx- 温度控件ToggleSwitch.tsx- 切换开关控件Slider.tsx- 通用滑块控件DropdownSelector.tsx- 下拉选择器TextInput.tsx- 文本输入控件
创建自定义参数控件
- 在
components/ParamControls/目录下创建新组件 - 实现参数控件接口
- 处理参数值变化
- 从
components/ParamControls/index.ts导出控件
示例 - 自定义滑块控件:
// components/ParamControls/CustomSlider.tsx
import React, { useState } from "react";
import { View, Text, StyleSheet } from "react-native";
import { Slider } from "@miblanchard/react-native-slider";
import { tokens } from "@/theme/tokens";
interface CustomSliderProps {
value: number;
min: number;
max: number;
step: number;
onValueChange: (value: number) => void;
label: string;
}
export const CustomSlider: React.FC<CustomSliderProps> = ({
value,
min,
max,
step,
onValueChange,
label,
}) => {
const [localValue, setLocalValue] = useState(value);
const handleValueChange = (values: number[]) => {
setLocalValue(values[0]);
};
const handleSlidingComplete = (values: number[]) => {
onValueChange(values[0]);
};
return (
<View style={styles.container}>
<Text style={styles.label}>{label}</Text>
<Slider
value={localValue}
minimumValue={min}
maximumValue={max}
step={step}
onValueChange={handleValueChange}
onSlidingComplete={handleSlidingComplete}
minimumTrackTintColor={tokens.colors.primary}
maximumTrackTintColor={tokens.colors.lightGray}
/>
<Text style={styles.value}>{Math.round(localValue)}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 16,
},
label: {
fontSize: 16,
marginBottom: 8,
},
value: {
textAlign: "center",
fontSize: 14,
marginTop: 8,
},
});
自定义适配器
ESP RainMaker App SDK 支持通过适配器扩展自定义功能。SDK 提供了接口,可以实现适配器以满足功能需求。
自定义存储适配器
存储适配器处理所有键值存储操作,位于 adaptors/implementations/ESPAsyncStorage.ts 中:
import AsyncStorage from "@react-native-async-storage/async-storage";
import { ESPRMStorageAdapterInterface } from "@espressif/rainmaker-base-sdk";
export const asyncStorageAdapter: ESPRMStorageAdapterInterface = {
setItem: async (name: string, value: string) => {
try {
await AsyncStorage.setItem(name, value);
} catch (error) {
throw error;
}
},
getItem: async (name: string): Promise<string | null> => {
try {
const response = await AsyncStorage.getItem(name);
return response;
} catch (error) {
throw error;
}
},
removeItem: async (name: string) => {
try {
await AsyncStorage.removeItem(name);
} catch (error) {
throw error;
}
},
clear: async () => {
try {
await AsyncStorage.clear();
} catch (error) {
throw error;
}
},
};
export default asyncStorageAdapter;
创建自定义存储适配器:
- 实现
ESPRMStorageAdapterInterface接口 - 为
setItem、getItem、removeItem和clear提供具体实现 - 在
rainmaker.config.ts中注册适配器
自定义通知适配器
通知适配器用于处理推送通知功能,位于 adaptors/implementations/ESPNotificationAdapter.ts 中:
import { DeviceEventEmitter, EmitterSubscription } from "react-native";
import ESPNotificationModule from "../interfaces/ESPNotificationInterface";
export const ESPNotificationAdapter = {
currentListener: null as EmitterSubscription | null,
addNotificationListener: async (
callback: (data: Record<string, any>) => void
): Promise<() => void> => {
try {
if (ESPNotificationAdapter.currentListener) {
ESPNotificationAdapter.removeNotificationListener();
}
// 监听并处理传入的通知
const notificationListener = DeviceEventEmitter.addListener(
"ESPNotificationModule",
(data: Record<string, any>) => {
callback(data); // 使用接收到的数据调用回调
}
);
// 保存监听器引用以便移除
ESPNotificationAdapter.currentListener = notificationListener;
// 返回清理函数以移除通知监听器
return () => {
ESPNotificationAdapter.removeNotificationListener();
};
} catch (error) {
return () => {}; // 发生错误时返回空操作清理函数
}
},
removeNotificationListener: (): void => {
try {
if (ESPNotificationAdapter.currentListener) {
ESPNotificationAdapter.currentListener.remove();
ESPNotificationAdapter.currentListener = null;
}
} catch (error) {
throw error;
}
},
getNotificationPlatform: async (): Promise<string> => {
try {
const platform = await ESPNotificationModule.getNotificationPlatform();
return platform;
} catch (error) {
console.error("Error getting notification platform:", error);
throw error;
}
},
};
创建自定义通知适配器:
- 实现通知适配器接口
- 为
addNotificationListener、removeNotificationListener和getNotificationPlatform提供具体实现 - 在
rainmaker.config.ts中注册适配器
适配器注册
创建自定义适配器后,在 rainmaker.config.ts 中进行注册:
import { customStorageAdapter } from "@/adaptors/implementations/CustomStorage";
import { customNotificationAdapter } from "@/adaptors/implementations/CustomNotifications";
export const SDKConfig = {
baseUrl: "https://api.rainmaker.espressif.com",
version: "v1",
authUrl: "https://3pauth.rainmaker.espressif.com",
clientId: "1h7ujqjs8140n17v0ahb4n51m2",
redirectUrl: "rainmaker://com.espressif.novahome/success",
customStorageAdapter: customStorageAdapter, // 自定义适配器
notificationAdapter: customNotificationAdapter, // 自定义适配器
// ... 其他适配器
};
⚠️ 重要声明:本仓库中提供的公共部署详情和配置仅用于开发和教育目的,不得用于商业用途。