跳到主要内容

适配器

@espressif/rainmaker-base-sdk 采用适配器模式,提供灵活的跨平台架构。这种设计使 SDK 能够通过适配器实现特定平台的功能,从而支持不同的 JavaScript 框架和平台(React Native、Web、Electron 等)。

为什么使用适配器?

适配器在 SDK 的核心 TypeScript 逻辑和特定平台实现之间架起了桥梁。不同平台功能有所不同:

  • React Native 需要原生模块来支持蓝牙、存储和通知功能
  • Web 应用 使用浏览器 API,如 localStorage 和 Web Bluetooth
  • Electron 应用 可能使用 Node.js 文件系统 API

通过使用适配器,SDK 可以:

  • 保持平台无关性
  • 在所有平台上提供一致的 API
  • 允许开发者根据特定需求提供自定义实现
  • 支持多种 UI 框架(React Native、Vue、Angular 等)

可用的适配器

SDK 为七种不同的适配器提供接口,每种适配器都有特定的用途:

1. 存储适配器

功能:管理用户令牌、配置和 SDK 状态的持久化数据存储。

接口ESPRMStorageAdapterInterface

必需方法

interface ESPRMStorageAdapterInterface {
setItem(name: string, value: string): Promise<void>;
getItem(name: string): Promise<string | null>;
removeItem(name: string): Promise<void>;
clear(): Promise<void>;
}

使用场景:始终必需。SDK 需要持久化存储来维护用户会话、缓存数据和存储配置。

常见实现

  • WeblocalStoragesessionStorage
  • React NativeAsyncStoragereact-native-mmkv
  • Node.js:文件系统或数据库存储

2. 配网适配器

功能:启用设备配网功能,允许用户将新的 ESP 设备接入 Wi-Fi 网络。

接口ESPProvisionAdapterInterface

必需方法

interface ESPProvisionAdapterInterface {
searchESPDevices(
devicePrefix: string,
transport: ESPTransport
): Promise<ESPDeviceInterface[]>;
stopESPDevicesSearch(): Promise<void>;
createESPDevice(
name: string,
transport: string,
security?: number,
proofOfPossession?: string,
softAPPassword?: string,
username?: string
): Promise<ESPDeviceInterface>;
connect(deviceName: string): Promise<ESPConnectStatus>;
getDeviceCapabilities(deviceName: string): Promise<string[]>;
getDeviceVersionInfo(deviceName: string): Promise<{ [key: string]: any }>;
setProofOfPossession(
deviceName: string,
proofOfPossession: string
): Promise<boolean>;
initializeSession(deviceName: string): Promise<boolean>;
scanWifiList(deviceName: string): Promise<ESPWifiList[]>;
sendData(deviceName: string, endPoint: string, data: string): Promise<string>;
provision(
deviceName: string,
ssid: string,
passphrase: string
): Promise<ESPProvisionStatus>;
disconnect(deviceName: string): Promise<void>;
}

使用场景:如果应用需要配网功能则必需。该适配器负责处理设备接入过程中与 ESP 设备的通信。

常见实现

  • React Native:用于 BLE/Wi-Fi 配网的原生模块
  • Web:Web Bluetooth API(用于 BLE 配网)

3. 本地发现适配器

功能:使用 mDNS/Bonjour 在本地网络上发现 ESP RainMaker 设备。

接口ESPLocalDiscoveryAdapterInterface

必需方法

interface ESPLocalDiscoveryAdapterInterface {
startDiscovery(callback: Function, params: DiscoveryParamsInterface): void;
stopDiscovery(): void;
}

interface DiscoveryParamsInterface {
serviceType: string; // 例如 "_esp_local_ctrl._tcp"
domain: string; // 例如 "local."
}

使用场景:可选。如果应用支持设备的本地控制(无需云连接即可控制设备),则需要此适配器。

常见实现

  • React Native:使用平台 mDNS/Bonjour API 的原生模块
  • Node.jsbonjourmdns
  • Web:支持有限(没有标准浏览器 API)

4. 本地控制适配器

功能:支持与本地网络上的设备直接通信,无需通过云端。

接口ESPLocalControlAdapterInterface

必需方法

interface ESPLocalControlAdapterInterface {
isConnected(nodeId: string): Promise<boolean>;
connect(
nodeId: string,
baseUrl: string,
securityType: number,
pop?: string,
username?: string
): Promise<Record<string, any>>;
sendData(nodeId: string, path: string, data: string): Promise<string>;
}

使用场景:可选。如果应用支持本地控制功能,在同一网络内实现更快速、更可靠的设备控制,则需要此适配器。

常见实现

  • React Native:用于安全本地通信的原生模块
  • Web/Node.js:实现安全协议的 HTTP/HTTPS 客户端

5. 通知适配器

功能:处理来自 ESP RainMaker 平台的推送通知。

接口ESPNotificationAdapterInterface

必需方法

interface ESPNotificationAdapterInterface {
addNotificationListener(callback: Function): void;
}

使用场景:可选。如果应用需要接收和处理有关设备事件、警报或自动化触发器的推送通知,则需要此适配器。

常见实现

  • React Native:Firebase Cloud Messaging (FCM) 或 Apple Push Notification Service (APNS)
  • Web:Web Push API 或 Firebase Cloud Messaging

6. OAuth 适配器

功能:为用户登录启用第三方身份验证(Google、Apple、GitHub 等)。

接口ESPOauthAdapterInterface

必需方法

interface ESPOauthAdapterInterface {
getCode(requestURL: string): Promise<string>;
}

使用场景:可选。仅当应用支持第三方登录方式时才需要。该适配器打开 OAuth 流程并返回授权码。

常见实现

  • React Native:应用内浏览器或原生 OAuth 模块
  • Web:弹出窗口或基于重定向的 OAuth 流程

7. 应用工具适配器

功能:提供用于权限检查和应用功能的平台特定工具函数。

接口ESPAppUtilityAdapterInterface

必需方法

interface ESPAppUtilityAdapterInterface {
isBlePermissionGranted(): Promise<boolean>;
isLocationPermissionGranted(): Promise<boolean>;
}

使用场景:可选。用于在尝试设备配置或发现前检查所需权限。

常见实现

  • React Native:检查平台权限的原生模块
  • Web:浏览器权限 API

实现适配器

基本示例:存储适配器

以下是为 Web 应用实现存储适配器的简单示例:

import { ESPRMStorageAdapterInterface } from "@espressif/rainmaker-base-sdk";

class WebStorageAdapter implements ESPRMStorageAdapterInterface {
async setItem(name: string, value: string): Promise<void> {
window.localStorage.setItem(name, value);
}

async getItem(name: string): Promise<string | null> {
return window.localStorage.getItem(name);
}

async removeItem(name: string): Promise<void> {
window.localStorage.removeItem(name);
}

async clear(): Promise<void> {
window.localStorage.clear();
}
}

export default new WebStorageAdapter();

React Native 示例:AsyncStorage 适配器

import AsyncStorage from "@react-native-async-storage/async-storage";
import { ESPRMStorageAdapterInterface } from "@espressif/rainmaker-base-sdk";

class AsyncStorageAdapter implements ESPRMStorageAdapterInterface {
async setItem(name: string, value: string): Promise<void> {
await AsyncStorage.setItem(name, value);
}

async getItem(name: string): Promise<string | null> {
return await AsyncStorage.getItem(name);
}

async removeItem(name: string): Promise<void> {
await AsyncStorage.removeItem(name);
}

async clear(): Promise<void> {
await AsyncStorage.clear();
}
}

export default new AsyncStorageAdapter();

React Native 参考实现

ESP RainMaker Home 仓库中提供了适用于 React Native 的所有适配器的完整参考实现:

仓库esp-rainmaker-home/adaptors

此实现包括:

  1. ESPAsyncStorage - 基于 AsyncStorage 的存储适配器
  2. ESPProvAdapter - 使用原生模块的 BLE/Wi-Fi 配网
  3. ESPDiscoveryAdapter - 使用原生模块的 mDNS 发现
  4. ESPLocalControlAdapter - 本地控制实现
  5. ESPNotificationAdapter - 推送通知处理
  6. ESPOauthAdapter - OAuth 身份验证流程
  7. ESPAppUtilityAdapter - 权限检查和工具

上述可以作为 React Native 应用的起点,或作为其他框架适配器实现的参考。


配置适配器

实现适配器后,使用 SDK 进行配置:

import { ESPRMBase } from "@espressif/rainmaker-base-sdk";
import storageAdapter from "./adapters/storage";
import provisionAdapter from "./adapters/provision";
import localDiscoveryAdapter from "./adapters/localDiscovery";
import localControlAdapter from "./adapters/localControl";
import notificationAdapter from "./adapters/notification";
import oauthAdapter from "./adapters/oauth";
import appUtilityAdapter from "./adapters/appUtility";

const config = {
baseUrl: "https://api.rainmaker.espressif.com",
version: "v1",

// 必需适配器
customStorageAdapter: storageAdapter,

// 可选适配器(仅提供应用所需的适配器)
provisionAdapter: provisionAdapter,
localDiscoveryAdapter: localDiscoveryAdapter,
localControlAdapter: localControlAdapter,
notificationAdapter: notificationAdapter,
oauthAdapter: oauthAdapter,
appUtilityAdapter: appUtilityAdapter,
};

ESPRMBase.configure(config);
说明

用户只需提供应用使用的功能所需的适配器。如果不需要配网或本地控制功能,可以省略这些适配器。


不同框架的适配器实现

Vue.js/Nuxt

对于 Vue.js 应用,可以采用与 React 类似的方式实现适配器:

// 使用 localStorage 的存储适配器
export const vueStorageAdapter = {
async setItem(name: string, value: string): Promise<void> {
localStorage.setItem(name, value);
},
async getItem(name: string): Promise<string | null> {
return localStorage.getItem(name);
},
async removeItem(name: string): Promise<void> {
localStorage.removeItem(name);
},
async clear(): Promise<void> {
localStorage.clear();
},
};

Angular

Angular 服务可以封装适配器实现:

import { Injectable } from "@angular/core";
import { ESPRMStorageAdapterInterface } from "@espressif/rainmaker-base-sdk";

@Injectable({
providedIn: "root",
})
export class StorageAdapterService implements ESPRMStorageAdapterInterface {
async setItem(name: string, value: string): Promise<void> {
localStorage.setItem(name, value);
}

async getItem(name: string): Promise<string | null> {
return localStorage.getItem(name);
}

async removeItem(name: string): Promise<void> {
localStorage.removeItem(name);
}

async clear(): Promise<void> {
localStorage.clear();
}
}

Electron

对于 Electron 应用,可能需要使用 Node.js API:

import Store from "electron-store";
import { ESPRMStorageAdapterInterface } from "@espressif/rainmaker-base-sdk";

const store = new Store();

export const electronStorageAdapter: ESPRMStorageAdapterInterface = {
async setItem(name: string, value: string): Promise<void> {
store.set(name, value);
},
async getItem(name: string): Promise<string | null> {
return store.get(name) as string | null;
},
async removeItem(name: string): Promise<void> {
store.delete(name);
},
async clear(): Promise<void> {
store.clear();
},
};

最佳实践

1. 错误处理

始终在适配器中实现适当的错误处理:

async setItem(name: string, value: string): Promise<void> {
try {
await AsyncStorage.setItem(name, value);
} catch (error) {
console.error('保存数据失败:', error);
throw error;
}
}

2. 类型安全

使用 TypeScript 的类型系统确保适配器实现所有必需方法:

import { ESPRMStorageAdapterInterface } from "@espressif/rainmaker-base-sdk";

// TypeScript 将强制要求实现所有方法
class MyStorageAdapter implements ESPRMStorageAdapterInterface {
// ... 实现
}

3. 平台检测

对于跨平台应用,检测平台并提供适当的适配器:

import webStorageAdapter from "./adapters/web-storage";
import reactNativeStorageAdapter from "./adapters/rn-storage";

const storageAdapter =
Platform.OS === "web" ? webStorageAdapter : reactNativeStorageAdapter;

ESPRMBase.configure({
// ...
customStorageAdapter: storageAdapter,
});

故障排除

缺少适配器错误

如果看到有关缺少适配器的错误,请确保在配置中提供了该适配器:

// ❌ 缺少存储适配器
ESPRMBase.configure({
baseUrl: "https://api.rainmaker.espressif.com",
});

// ✅ 已提供存储适配器
ESPRMBase.configure({
baseUrl: "https://api.rainmaker.espressif.com",
customStorageAdapter: storageAdapter,
});

接口不匹配

如果 TypeScript 报告适配器与接口不匹配,请确保所有方法都以正确的签名实现:

// ❌ 缺少 async/await
class BadAdapter implements ESPRMStorageAdapterInterface {
setItem(name: string, value: string): void {
// 缺少 Promise 返回类型
localStorage.setItem(name, value);
}
}

// ✅ 正确实现
class GoodAdapter implements ESPRMStorageAdapterInterface {
async setItem(name: string, value: string): Promise<void> {
localStorage.setItem(name, value);
}
}