Introduction
Once you are done with the basic customizations, you may wish to add more features or modify the behaviors, customize UI control panels, and so on to meet your product and app needs.
This section talks about understanding how you can customize a device control panel screen or how you can customize parameter control. You can even extend the existing behavior of notifications.
Pre-Requisite
Assumption is that you already have the app development setup ready. Follow the instructions for setting up the development environment. All the references below would be based on the cloned GitHub repository for the sample ESP RainMaker Home app source code.
Assumption is that you are already aware of how hybrid apps work and are familiar with React Native & Expo app development process.
API Configuration
Custom API Endpoint
Customize the ESP RainMaker API endpoint and authentication settings in rainmaker.config.ts:
export const SDKConfig = {
baseUrl: "https://your-custom-api.com", // Custom API endpoint
version: "v1",
authUrl: "https://your-custom-auth.com", // Custom OAuth endpoint
clientId: "your-client-id", // OAuth client ID
redirectUrl: "yourapp://com.yourcompany.yourapp/success", // OAuth redirect URL
customStorageAdapter: asyncStorageAdapter,
localDiscoveryAdapter: EspLocalDiscoveryAdapter,
localControlAdapter: ESPLocalControlAdapter,
provisionAdapter: provisionAdapter,
notificationAdapter: ESPNotificationAdapter,
oauthAdapter: espOauthAdapter,
appUtilityAdapter: ESPAppUtilityAdapter,
};
Environment-Specific Configuration
You can create environment-specific configurations:
// Development configuration
const developmentConfig = {
baseUrl: "https://api.staging.rainmaker.espressif.com",
};
// Production configuration
const productionConfig = {
baseUrl: "https://api.rainmaker.espressif.com",
};
// Use appropriate configuration based on environment
export const SDKConfig = __DEV__ ? developmentConfig : productionConfig;
Custom Device Control Panel Screen
Go to your source code and create custom device control panels in app/(device)/device_panels/ by creating new panel components.
Existing device panels:
Light.tsx- Light device control panelSwitch.tsx- Switch device control panelTemperature.tsx- Temperature sensor panelFallback.tsx- Default fallback panel
Steps to Create a Custom Device Panel
- Create a new TypeScript file in
app/(device)/device_panels/ - Import necessary components and hooks
- Implement custom UI and control logic
- Register the panel in the device configuration
Example - Custom Light Panel Structure:
// 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} />
{/* Add your custom controls here */}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
});
export default observer(CustomLightPanel);
Custom Parameter Controls
Add new parameter control types in components/ParamControls/ to support custom device parameters.
Existing parameter controls:
PowerButton.tsx- Power on/off controlBrightnessSlider.tsx- Brightness controlHueSlider.tsx- Color hue controlSaturationSlider.tsx- Color saturation controlColorTemperatureSlider.tsx- Color temperature controlSpeedSlider.tsx- Fan speed controlTemperatureSlider.tsx- Temperature controlToggleSwitch.tsx- Toggle switch controlSlider.tsx- Generic slider controlDropdownSelector.tsx- Dropdown selectorTextInput.tsx- Text input control
Steps to Create a Custom Parameter Control
- Create a new component in
components/ParamControls/ - Implement the parameter control interface
- Handle parameter value changes
- Export the control from
components/ParamControls/index.ts
Example - Custom Slider Control:
// 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,
},
});
Custom Adapters
ESP RainMaker App SDK has a provision to extend the functionality with custom behavior via adapters. The SDK provides interfaces to implement adapters to suit your functional needs.
Custom Storage Adapter
The storage adapter handles all key-value storage operations. Located in 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;
To create a custom storage adapter:
- Implement the
ESPRMStorageAdapterInterface - Provide implementations for
setItem,getItem,removeItem, andclear - Register your adapter in
rainmaker.config.ts
Custom Notifications Adapter
The notification adapter handles push notification functionality. Located in 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();
}
// Listen for incoming notifications and handle them
const notificationListener = DeviceEventEmitter.addListener(
"ESPNotificationModule",
(data: Record<string, any>) => {
callback(data); // Invoke the callback with the received data
}
);
// Save listener reference for removal
ESPNotificationAdapter.currentListener = notificationListener;
// Return a cleanup function to remove the notification listener
return () => {
ESPNotificationAdapter.removeNotificationListener();
};
} catch (error) {
return () => {}; // Return a no-op cleanup function in case of an error
}
},
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;
}
},
};
To create a custom notification adapter:
- Implement the notification adapter interface
- Provide implementations for
addNotificationListener,removeNotificationListener, andgetNotificationPlatform - Register your adapter in
rainmaker.config.ts
Adapter Registration
After creating custom adapters, register them in 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, // Your custom adapter
notificationAdapter: customNotificationAdapter, // Your custom adapter
// ... other adapters
};
⚠️ IMPORTANT NOTICE: The public deployment details and configurations provided in this repository are intended for development and educational purposes only and should NOT be used for commercial purposes.