Skip to main content

Events and Subscriptions

The SDK provides an event-driven architecture that allows you to subscribe to real-time updates about your nodes and devices. This includes local network discovery events and node update notifications.

note

The userInstance used in this documentation refers to ESPRMUser class instance obtained in User Sign in step.

Overview

The event subscription system enables:

  • Real-time local discovery: Automatically detect when nodes become available on the local network
  • Transport management: Update node transport configurations dynamically
  • Node updates: Receive notifications about node state changes
  • Flexible callbacks: Register custom handlers for different event types

Event Types

The SDK supports the following event types through the ESPRMEventType enum:

Event TypeDescription
localDiscoveryTriggered when nodes are discovered on the local network. Used to update node transport configurations.
nodeUpdatesTriggered when node state changes occur (parameter updates, connectivity changes, etc.).

Local Discovery and Transport Updates

How It Works

The local discovery mechanism follows this flow:

Step 1: Register Local Discovery Callback

To receive local discovery events, register a callback using the subscribe method:

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

// Define your callback to handle discovered nodes
const localDiscoveryCallback = (discoveredNodeData) => {
console.log("Node discovered locally:", discoveredNodeData);

// discoveredNodeData contains:
// - nodeId: The ID of the discovered node
// - transportDetails: Transport configuration including baseUrl

const { nodeId, transportDetails } = discoveredNodeData;

// Update your node store with the new transport information
// This allows the node to communicate over local network
updateNodeTransport(nodeId, transportDetails);
};

// Subscribe to local discovery events
try {
userInstance.subscribe(ESPRMEventType.localDiscovery, localDiscoveryCallback);
console.log("Subscribed to local discovery events");
} catch (error) {
console.error("Error subscribing to local discovery:", error);
}
Important: Node Store Access

Your callback should have access to your node store (or equivalent state management system) so that when a node is discovered locally, you can update the node's availableTransports field with the local transport details. The availableTransports is a Record<ESPTransportMode, ESPTransportConfig> where each transport mode maps to its configuration.

Step 2: SDK Starts Discovery

When you call the subscribe method with ESPRMEventType.localDiscovery:

  1. SDK registers the callback: Your callback is stored in the internal eventCallbacks registry
  2. Discovery manager is created: An instance of ESPDiscoveryManager is created with the local protocol
  3. Discovery starts: The manager calls startDiscovery() which uses the ESPDiscoveryAdapter to scan the local network
  4. Callbacks are triggered: When nodes are discovered, your callback is invoked with the node details
// This happens automatically inside the SDK when you subscribe
// You don't need to call these directly - just subscribe!

// Internal SDK flow (for reference):
// 1. eventCallbacks[ESPRMEventType.localDiscovery].push(callback)
// 2. new ESPDiscoveryManager(ESPDiscoveryProtocol.local)
// 3. localDiscoveryManager.startDiscovery(discoveryCallback)
Discovery Adapter Required

Local discovery requires the ESPDiscoveryAdapter to be configured during SDK initialization. Learn more about adapters.

Step 3: Update Node Transport

When your callback receives discovery events, update the node's available transports:

// Example: Update node transport in your state management
const localDiscoveryCallback = (discoveredNodeData) => {
const { nodeId, transportDetails } = discoveredNodeData;

// transportDetails structure:
// {
// type: "local",
// metadata: {
// baseUrl: "http://192.168.1.100" // Local IP of the node
// }
// }

// Find the node in your store
const node = findNodeById(nodeId);

if (node) {
// Initialize availableTransports as a Record if it doesn't exist
node.availableTransports = node.availableTransports || {};

// Add or update the local transport configuration
// availableTransports is a Record<ESPTransportMode, ESPTransportConfig>
node.availableTransports[transportDetails.type] = transportDetails;

console.log(
`Node ${nodeId} is now available locally at ${transportDetails.metadata.baseUrl}`
);
}
};

Transport Priority

Setting Transport Order

The setTransportOrder method defines the priority of transports the SDK should use when communicating with nodes (e.g., setting device parameters).

import { ESPRMBase, ESPTransportMode } from "@espressif/rainmaker-base-sdk";

// Define transport priority: try local first, fallback to cloud
const transportOrder = [
ESPTransportMode.local, // First priority
ESPTransportMode.cloud, // Fallback
];

try {
ESPRMBase.setTransportOrder(transportOrder);
console.log("Transport order set successfully");
} catch (error) {
console.error("Error setting transport order:", error);
}
Transport Priority Strategy
  • Local First: Set [ESPTransportMode.local, ESPTransportMode.cloud] for faster response times when on the same network
  • Cloud First: Set [ESPTransportMode.cloud, ESPTransportMode.local] for consistent behavior regardless of network location

How Transport Order Works with Discovery

  1. Discovery updates availableTransports: When local discovery finds a node, it adds local transport to the node's availableTransports record using the transport mode as the key
  2. SDK checks transport order: When you set a device parameter, the SDK checks the transport order you've defined
  3. SDK attempts communication: The SDK tries each transport in order based on what's available in the availableTransports record
  4. Fallback mechanism: If the first transport fails, the SDK automatically falls back to the next available transport
// Example workflow:
// 1. Set transport order to prefer local
ESPRMBase.setTransportOrder([ESPTransportMode.local, ESPTransportMode.cloud]);

// 2. Subscribe to local discovery to populate availableTransports
userInstance.subscribe(ESPRMEventType.localDiscovery, (data) => {
updateNodeTransport(data.nodeId, data.transportDetails);
});

// 3. When you set a parameter, SDK automatically uses local transport if available
await device.setParamValue("power", true);
// SDK checks: node.availableTransports[ESPTransportMode.local] exists? Use it!
// If local fails or unavailable, SDK falls back to cloud

Node Update Events

Subscribe to node update events to receive real-time notifications about node state changes:

const nodeUpdateCallback = (updateData) => {
console.log("Node update received:", updateData);

// updateData contains information about:
// - Which node was updated
// - What parameters changed
// - New parameter values

// Update your UI or state accordingly
handleNodeUpdate(updateData);
};

try {
userInstance.subscribe(ESPRMEventType.nodeUpdates, nodeUpdateCallback);
console.log("Subscribed to node updates");
} catch (error) {
console.error("Error subscribing to node updates:", error);
}
Notification Adapter Required

Node update events require the ESPNotificationAdapter to be configured for push notifications. Learn more about push notifications.

Managing Subscriptions

Subscribe to Events

Subscribe a single or multiple callbacks to an event:

// Subscribe single callback
userInstance.subscribe(ESPRMEventType.localDiscovery, callback1);

// Subscribe multiple callbacks at once
userInstance.subscribe(ESPRMEventType.localDiscovery, [callback1, callback2]);

Unsubscribe from Events

Remove a specific callback from an event:

userInstance.unsubscribe(ESPRMEventType.localDiscovery, callback1);

Remove All Callbacks

Remove all callbacks for a specific event or all events:

// Remove all callbacks for a specific event
userInstance.removeAllCallbacks(ESPRMEventType.localDiscovery);

// Remove all callbacks for all events
userInstance.removeAllCallbacks();

Complete Example

Here's a complete example showing how to set up event subscriptions for a typical application:

import {
ESPRMBase,
ESPRMEventType,
ESPTransportMode,
} from "@espressif/rainmaker-base-sdk";

// Step 1: Set transport order (prefer local for speed)
ESPRMBase.setTransportOrder([ESPTransportMode.local, ESPTransportMode.cloud]);

// Step 2: Define local discovery callback
const handleLocalDiscovery = (discoveredNodeData) => {
const { nodeId, transportDetails } = discoveredNodeData;

console.log(`Node ${nodeId} discovered on local network`);
console.log(`Base URL: ${transportDetails.metadata.baseUrl}`);

// Update your state/store with the new transport
updateNodeAvailableTransports(nodeId, transportDetails);
};

// Step 3: Define node update callback
const handleNodeUpdates = (updateData) => {
console.log("Node state changed:", updateData);

// Update your UI with the latest node data
refreshNodeData(updateData);
};

// Step 4: Subscribe to events
try {
// Subscribe to local discovery
userInstance.subscribe(ESPRMEventType.localDiscovery, handleLocalDiscovery);

// Subscribe to node updates
userInstance.subscribe(ESPRMEventType.nodeUpdates, handleNodeUpdates);

console.log("All event subscriptions active");
} catch (error) {
console.error("Error setting up subscriptions:", error);
}

// Step 5: Cleanup when done (e.g., on app unmount)
const cleanup = () => {
userInstance.removeAllCallbacks(ESPRMEventType.localDiscovery);
userInstance.removeAllCallbacks(ESPRMEventType.nodeUpdates);
};

Best Practices

  1. Keep callbacks lightweight: Callbacks should update state/store quickly without blocking the event loop
  2. Handle errors gracefully: Always wrap state updates in try-catch blocks
  3. Clean up subscriptions: Remove callbacks when components unmount or when they're no longer needed
  4. Use appropriate transport order: Choose transport priority based on your use case (local vs cloud first)
  5. Update availableTransports properly: Ensure your callback has access to node store to update transport configurations. Remember that availableTransports is a Record<ESPTransportMode, ESPTransportConfig>, not an array
  6. Test both scenarios: Test your app when nodes are on local network and when they're only accessible via cloud

On this page