The Local Control module provides functionality for establishing direct communication with ESP devices on the local network, bypassing the ESP RainMaker Cloud. This enables faster response times and offline control capabilities.
Local Control is implemented across three layers:
- Native Layer: Handles platform-specific session management and secure communication (ESPProvision SDK for iOS, ESP Provisioning SDK for Android)
- SDK Layer: Provides local control adapters and React Native bridge integration
- Application Layer: Manages device connections and data exchange
Local Control requires the device to be on the same local network as the mobile app and supports multiple security levels for secure communication.
Local Control
This guide explains how the Local Control module works, focusing on session establishment, security types, data transmission, and how it integrates with the native layer, SDK, and application layer.
User Flow Overview
The Local Control module has three main flows:
- Connection Establishment: App requests connection → Native module establishes session → Security handshake → Session ready
- Connection Check: App checks if device is connected → Native module verifies session status → Returns connection state
- Data Transmission: App sends command → Native module encrypts data → Sends to device → Receives response → Decrypts and returns
Architecture Overview
Components Structure
Native Layer (iOS):
ios/
└── Local Control/
├── ESPLocalControlModule.swift # React Native bridge for iOS local control
└── ESPLocalControlModule.m # Objective-C bridge exports
Native Layer (Android):
android/app/src/main/java/com/app/local_control/
└── ESPLocalControlModule.kt # React Native bridge for Android local control
SDK Layer:
adaptors/
├── interfaces/
│ └── ESPLocalControlInterface.ts # Native module interface
└── implementations/
└── ESPLocalControlAdapter.ts # Local control adapter for SDK integration
1. Connection Establishment Process
The connection establishment process creates a secure session with an ESP device on the local network. This session enables encrypted communication for controlling the device.
Step-by-Step Connection Flow
iOS Connection Implementation
ESPLocalControlModule.swift handles iOS connection:
@objc(connect:baseUrl:securityType:pop:username:resolve:reject:)
func connect(nodeId: String, baseUrl: String, securityType: NSNumber, pop: String?, username: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
// Determine security type and configure ESPDevice
switch securityType {
case 1:
// Security1: Secure connection with proof of possession
if let pop = pop {
espLocalDevice = ESPDevice(name: nodeId, security: .secure, transport: .softap, proofOfPossession: pop)
} else {
reject("error", "Proof of possession is missing", nil)
return
}
case 2:
// Security2: Secure connection with proof of possession and username
if let pop = pop, let username = username {
espLocalDevice = ESPDevice(name: nodeId, security: .secure, transport: .softap, proofOfPossession: pop, username: username)
} else {
reject("error", "Username or password is missing", nil)
return
}
default:
// Security0: Unsecure connection
espLocalDevice = ESPDevice(name: nodeId, security: .unsecure, transport: .softap)
}
// Configure transport layer
espLocalDevice.espSoftApTransport = ESPSoftAPTransport(baseUrl: baseUrl)
// Initialize session
espLocalDevice.initialiseSession(sessionPath: sessionPath) { status in
switch status {
case .connected:
resolve(["status": "success"])
case .failedToConnect(let eSPSessionError):
reject("error", eSPSessionError.description, nil)
case .disconnected:
reject("error", "Failed to establish session", nil)
}
}
}
Android Connection Implementation
ESPLocalControlModule.kt handles Android connection:
@ReactMethod
fun connect(
nodeId: String,
baseUrl: String,
securityType: Int,
pop: String?,
username: String?,
promise: Promise
) {
this.securityType = securityType
this.baseUrl = baseUrl
// Parse baseUrl to extract IP address and port
val address: String
val port: Int
try {
val url = baseUrl.removePrefix("http://")
val urlParts = url.split(":")
address = urlParts[0]
port = urlParts[1].toInt()
} catch (e: Exception) {
promise.reject("INVALID_BASE_URL", "Failed to parse base URL: $baseUrl. Error: ${e.message}")
return
}
val device = EspLocalDevice(nodeId, address, port)
// Initialize session with security configuration
initSession(device, baseUrl, securityType, pop, username, object : ResponseListener {
override fun onSuccess(returnData: ByteArray?) {
localDeviceMap[nodeId] = device
val result = WritableNativeMap().apply {
putString("status", "success")
}
promise.resolve(result)
}
override fun onFailure(e: Exception) {
promise.reject("SESSION_ESTABLISHMENT_FAILED", "Failed to establish session for nodeId: $nodeId. Error: ${e.message}")
}
})
}
React Native Adapter
ESPLocalControlAdapter.ts provides the React Native interface:
connect: async (
nodeId: string,
baseurl: string,
securityType: number,
pop?: string,
username?: string
): Promise<Record<string, any>> => {
try {
// Default username for Security2 if not provided
let _username;
if (!username) {
_username = securityType === 2 ? "wifiprov" : "";
}
const res = await ESPLocalControlModule.connect(
nodeId,
baseurl,
securityType,
pop,
_username
);
return res;
} catch (error) {
throw error;
}
}
2. Security Types
Local Control supports three security levels for establishing secure sessions with ESP devices:
Security Type 0 (Unsecure)
- Description: No encryption, plain text communication
- Use Case: Development, testing, or devices on trusted networks
- Requirements: None
- iOS:
ESPDevice(name: nodeId, security: .unsecure, transport: .softap) - Android:
Security0()
Security Type 1 (Secure with POP)
- Description: Encrypted communication using proof of possession (POP)
- Use Case: Production devices requiring basic security
- Requirements: Proof of possession (POP) string
- iOS:
ESPDevice(name: nodeId, security: .secure, transport: .softap, proofOfPossession: pop) - Android:
Security1(pop)
Security Type 2 (Secure with POP + Username)
- Description: Encrypted communication using proof of possession and username authentication
- Use Case: Production devices requiring enhanced security
- Requirements: Proof of possession (POP) string and username
- Default Username:
"wifiprov"(if not provided) - iOS:
ESPDevice(name: nodeId, security: .secure, transport: .softap, proofOfPossession: pop, username: username) - Android:
Security2(username, pop)
Security Type Selection
The security type is typically determined by:
- Device configuration during provisioning
- Device capabilities
- Security requirements
// Example: Connect with Security Type 1
await ESPLocalControlAdapter.connect(
"node123",
"http://192.168.1.100:80",
1, // Security Type 1
"ABCD1234" // POP
);
// Example: Connect with Security Type 2
await ESPLocalControlAdapter.connect(
"node123",
"http://192.168.1.100:80",
2, // Security Type 2
"ABCD1234", // POP
"wifiprov" // Username (optional, defaults to "wifiprov")
);
3. Connection Status Check
The isConnected method checks if a device has an established session.