@espressif/rainmaker-neo-base-sdk 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +40 -4
- package/dist/cjs/ESPDevice.js +6 -0
- package/dist/cjs/ESPDevice.js.map +1 -1
- package/dist/cjs/ESPRMNeoBase.js +1 -1
- package/dist/cjs/ESPRMNeoNode.js +33 -14
- package/dist/cjs/ESPRMNeoNode.js.map +1 -1
- package/dist/cjs/index.js +14 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/methods/ESPDevice/Provision.js +7 -0
- package/dist/cjs/methods/ESPDevice/Provision.js.map +1 -1
- package/dist/cjs/methods/ESPDevice/ResetWifiStatus.js +26 -0
- package/dist/cjs/methods/ESPDevice/ResetWifiStatus.js.map +1 -0
- package/dist/cjs/methods/ESPDevice/RetryNetworkCredentials.js +46 -0
- package/dist/cjs/methods/ESPDevice/RetryNetworkCredentials.js.map +1 -0
- package/dist/cjs/methods/ESPRMNeoNode/Sync.js +4 -1
- package/dist/cjs/methods/ESPRMNeoNode/Sync.js.map +1 -1
- package/dist/cjs/methods/ESPRMNeoUser/MQTT/AssumeRole.js +8 -9
- package/dist/cjs/methods/ESPRMNeoUser/MQTT/AssumeRole.js.map +1 -1
- package/dist/cjs/proto/esp_rmaker_chal_resp.js +5 -0
- package/dist/cjs/proto/esp_rmaker_chal_resp.js.map +1 -1
- package/dist/cjs/proto/rmaker_local_ctrl.js +273 -0
- package/dist/cjs/proto/rmaker_local_ctrl.js.map +1 -0
- package/dist/cjs/services/ESPRMNeoMqtt/ESPRMNeoMqtt.js +13 -0
- package/dist/cjs/services/ESPRMNeoMqtt/ESPRMNeoMqtt.js.map +1 -1
- package/dist/cjs/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js +5 -4
- package/dist/cjs/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js.map +1 -1
- package/dist/cjs/services/ESPTransport/ESPLocalControlTransport.js +183 -116
- package/dist/cjs/services/ESPTransport/ESPLocalControlTransport.js.map +1 -1
- package/dist/cjs/services/ESPTransport/LocalControlSession.js +62 -0
- package/dist/cjs/services/ESPTransport/LocalControlSession.js.map +1 -0
- package/dist/cjs/services/NodeMQTTOrchestrator.js +2 -5
- package/dist/cjs/services/NodeMQTTOrchestrator.js.map +1 -1
- package/dist/cjs/types/transport.js +24 -0
- package/dist/cjs/types/transport.js.map +1 -1
- package/dist/cjs/utils/constants.js +80 -4
- package/dist/cjs/utils/constants.js.map +1 -1
- package/dist/cjs/utils/eventSubscriptionUtils.js +48 -4
- package/dist/cjs/utils/eventSubscriptionUtils.js.map +1 -1
- package/dist/cjs/utils/nodeUtils.js +12 -1
- package/dist/cjs/utils/nodeUtils.js.map +1 -1
- package/dist/cjs/utils/waitForNodeOnline.js +7 -6
- package/dist/cjs/utils/waitForNodeOnline.js.map +1 -1
- package/dist/esm/ESPDevice.js +6 -0
- package/dist/esm/ESPDevice.js.map +1 -1
- package/dist/esm/ESPRMNeoBase.js +1 -1
- package/dist/esm/ESPRMNeoNode.js +33 -14
- package/dist/esm/ESPRMNeoNode.js.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/methods/ESPDevice/Provision.js +7 -0
- package/dist/esm/methods/ESPDevice/Provision.js.map +1 -1
- package/dist/esm/methods/ESPDevice/ResetWifiStatus.js +24 -0
- package/dist/esm/methods/ESPDevice/ResetWifiStatus.js.map +1 -0
- package/dist/esm/methods/ESPDevice/RetryNetworkCredentials.js +44 -0
- package/dist/esm/methods/ESPDevice/RetryNetworkCredentials.js.map +1 -0
- package/dist/esm/methods/ESPRMNeoNode/Sync.js +4 -1
- package/dist/esm/methods/ESPRMNeoNode/Sync.js.map +1 -1
- package/dist/esm/methods/ESPRMNeoUser/MQTT/AssumeRole.js +9 -10
- package/dist/esm/methods/ESPRMNeoUser/MQTT/AssumeRole.js.map +1 -1
- package/dist/esm/proto/esp_rmaker_chal_resp.js +5 -0
- package/dist/esm/proto/esp_rmaker_chal_resp.js.map +1 -1
- package/dist/esm/proto/rmaker_local_ctrl.js +271 -0
- package/dist/esm/proto/rmaker_local_ctrl.js.map +1 -0
- package/dist/esm/services/ESPRMNeoMqtt/ESPRMNeoMqtt.js +13 -0
- package/dist/esm/services/ESPRMNeoMqtt/ESPRMNeoMqtt.js.map +1 -1
- package/dist/esm/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js +5 -4
- package/dist/esm/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js.map +1 -1
- package/dist/esm/services/ESPTransport/ESPLocalControlTransport.js +184 -117
- package/dist/esm/services/ESPTransport/ESPLocalControlTransport.js.map +1 -1
- package/dist/esm/services/ESPTransport/LocalControlSession.js +59 -0
- package/dist/esm/services/ESPTransport/LocalControlSession.js.map +1 -0
- package/dist/esm/services/NodeMQTTOrchestrator.js +2 -5
- package/dist/esm/services/NodeMQTTOrchestrator.js.map +1 -1
- package/dist/esm/types/transport.js +24 -1
- package/dist/esm/types/transport.js.map +1 -1
- package/dist/esm/utils/constants.js +74 -5
- package/dist/esm/utils/constants.js.map +1 -1
- package/dist/esm/utils/eventSubscriptionUtils.js +49 -5
- package/dist/esm/utils/eventSubscriptionUtils.js.map +1 -1
- package/dist/esm/utils/nodeUtils.js +12 -1
- package/dist/esm/utils/nodeUtils.js.map +1 -1
- package/dist/esm/utils/waitForNodeOnline.js +7 -6
- package/dist/esm/utils/waitForNodeOnline.js.map +1 -1
- package/dist/types/ESPDevice.d.ts +10 -0
- package/dist/types/ESPRMNeoBase.d.ts +1 -1
- package/dist/types/ESPRMNeoNode.d.ts +17 -5
- package/dist/types/methods/ESPDevice/ResetWifiStatus.d.ts +18 -0
- package/dist/types/methods/ESPDevice/RetryNetworkCredentials.d.ts +22 -0
- package/dist/types/methods/ESPDevice/index.d.ts +2 -0
- package/dist/types/methods/ESPRMNeoUser/MQTT/AssumeRole.d.ts +25 -15
- package/dist/types/proto/rmaker_local_ctrl.d.ts +137 -0
- package/dist/types/services/ESPRMNeoMqtt/ESPRMNeoMqtt.d.ts +10 -0
- package/dist/types/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.d.ts +4 -3
- package/dist/types/services/ESPTransport/ESPLocalControlTransport.d.ts +70 -23
- package/dist/types/services/ESPTransport/LocalControlSession.d.ts +24 -0
- package/dist/types/services/NodeMQTTOrchestrator.d.ts +2 -2
- package/dist/types/services/interfaces/MQTTTransport.d.ts +10 -0
- package/dist/types/types/discovery.d.ts +1 -1
- package/dist/types/types/localControl.d.ts +26 -1
- package/dist/types/types/provision.d.ts +10 -0
- package/dist/types/types/transport.d.ts +24 -2
- package/dist/types/utils/baseUtils.d.ts +1 -1
- package/dist/types/utils/constants.d.ts +74 -5
- package/dist/types/utils/eventSubscriptionUtils.d.ts +19 -3
- package/dist/types/utils/waitForNodeOnline.d.ts +9 -3
- package/package.json +1 -1
- package/dist/cjs/proto/constants.js +0 -24
- package/dist/cjs/proto/constants.js.map +0 -1
- package/dist/cjs/proto/esp_local_ctrl.js +0 -787
- package/dist/cjs/proto/esp_local_ctrl.js.map +0 -1
- package/dist/esm/proto/constants.js +0 -24
- package/dist/esm/proto/constants.js.map +0 -1
- package/dist/esm/proto/esp_local_ctrl.js +0 -758
- package/dist/esm/proto/esp_local_ctrl.js.map +0 -1
- package/dist/types/proto/constants.d.ts +0 -15
- package/dist/types/proto/esp_local_ctrl.d.ts +0 -283
|
@@ -1,26 +1,70 @@
|
|
|
1
1
|
import { ESPDiscoveryManager } from '../services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js';
|
|
2
2
|
import { subscribeNodeUpdates } from '../services/NodeUpdatesBus.js';
|
|
3
|
-
import { ESPTransportMode } from '../types/transport.js';
|
|
3
|
+
import { ESPLocalControlProtocol, ESPTransportMode } from '../types/transport.js';
|
|
4
|
+
import { RMakerLocalCtrlCapability, RMakerLocalCtrlTxtKey } from './constants.js';
|
|
4
5
|
|
|
5
6
|
/*
|
|
6
7
|
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
7
8
|
*
|
|
8
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
9
10
|
*/
|
|
10
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Splits the mDNS `cap` TXT record (`"local_ctrl,ch_resp"`) into tokens.
|
|
13
|
+
* Returns `undefined` when the record is absent, which older firmware omits.
|
|
14
|
+
*/
|
|
15
|
+
function parseCapabilities(txt) {
|
|
16
|
+
const raw = txt?.[RMakerLocalCtrlTxtKey.CAP];
|
|
17
|
+
if (typeof raw !== "string")
|
|
18
|
+
return undefined;
|
|
19
|
+
const capabilities = raw
|
|
20
|
+
.split(",")
|
|
21
|
+
.map((capability) => capability.trim())
|
|
22
|
+
.filter(Boolean);
|
|
23
|
+
return capabilities.length ? capabilities : undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Maps a default local-discovery hit into the client-facing payload shape.
|
|
27
|
+
*
|
|
28
|
+
* The `_esp_rmaker_ctrl._tcp` instance is also advertised by nodes that
|
|
29
|
+
* only serve challenge-response (on-network user-node association). Those are
|
|
30
|
+
* not reachable for param control, so a hit whose `cap` TXT record excludes
|
|
31
|
+
* `local_ctrl` maps to `undefined` and is dropped rather than registered as a
|
|
32
|
+
* local transport. A hit with no `cap` record is treated as control-capable.
|
|
33
|
+
*
|
|
34
|
+
* @param info - Raw adapter result (`nodeId`, `baseUrl`, and `txt` when the
|
|
35
|
+
* platform resolved TXT records).
|
|
36
|
+
* @returns The payload to deliver, or `undefined` to skip this hit.
|
|
37
|
+
*/
|
|
11
38
|
function toDiscoveredNodeData(info) {
|
|
39
|
+
const capabilities = parseCapabilities(info.txt);
|
|
40
|
+
if (capabilities &&
|
|
41
|
+
!capabilities.includes(RMakerLocalCtrlCapability.LOCAL_CTRL)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
12
44
|
return {
|
|
13
45
|
nodeId: info.nodeId,
|
|
14
46
|
transportDetails: {
|
|
15
47
|
type: ESPTransportMode.local,
|
|
16
|
-
metadata: {
|
|
48
|
+
metadata: {
|
|
49
|
+
baseUrl: info.baseUrl,
|
|
50
|
+
protocol: ESPLocalControlProtocol.rmakerLocalCtrl,
|
|
51
|
+
...(capabilities && { capabilities }),
|
|
52
|
+
},
|
|
17
53
|
},
|
|
18
54
|
};
|
|
19
55
|
}
|
|
20
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Starts LAN discovery; each hit is mapped then passed to `onDiscovered`.
|
|
58
|
+
* Hits that are not control-capable (see {@link toDiscoveredNodeData}) are
|
|
59
|
+
* skipped.
|
|
60
|
+
*/
|
|
21
61
|
function startLocalDiscovery(onDiscovered) {
|
|
22
62
|
const manager = new ESPDiscoveryManager();
|
|
23
|
-
manager.startDiscovery((info) =>
|
|
63
|
+
manager.startDiscovery((info) => {
|
|
64
|
+
const data = toDiscoveredNodeData(info);
|
|
65
|
+
if (data)
|
|
66
|
+
onDiscovered(data);
|
|
67
|
+
});
|
|
24
68
|
return { stop: () => manager.stopDiscovery() };
|
|
25
69
|
}
|
|
26
70
|
/** Forwards process-wide node param updates to `onUpdate`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eventSubscriptionUtils.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"eventSubscriptionUtils.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -26,7 +26,18 @@ async function readLocalNodeConfig(nodeId) {
|
|
|
26
26
|
/** Best-effort persist of node config to local storage. */
|
|
27
27
|
async function writeLocalNodeConfig(nodeId, config) {
|
|
28
28
|
try {
|
|
29
|
-
|
|
29
|
+
let toStore = config;
|
|
30
|
+
// Cloud config omits connectivity — keep the last cached status when writing.
|
|
31
|
+
if (!config.connectivity_status) {
|
|
32
|
+
const prev = await ESPRMNeoStorage.getNodeConfig(nodeId);
|
|
33
|
+
if (prev?.connectivity_status) {
|
|
34
|
+
toStore = {
|
|
35
|
+
...config,
|
|
36
|
+
connectivity_status: prev.connectivity_status,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
await ESPRMNeoStorage.setNodeConfig(nodeId, toStore);
|
|
30
41
|
}
|
|
31
42
|
catch (error) {
|
|
32
43
|
logger.warn(`Cache write failed for ${nodeId}`, error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeUtils.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"nodeUtils.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -63,11 +63,12 @@ function isNodeOnlineFromShadowPayload(payload) {
|
|
|
63
63
|
* @throws {ESPProvError} If `timeoutMs` elapses without the node reporting online.
|
|
64
64
|
* @throws {Error} If MQTT connection or subscription fails.
|
|
65
65
|
*/
|
|
66
|
-
async function waitForNodeOnline({ nodeId, groupId, user, timeoutMs = DEFAULT_NODE_ONLINE_TIMEOUT_MS, pollIntervalMs = DEFAULT_NODE_ONLINE_POLL_INTERVAL_MS, }) {
|
|
67
|
-
const shadowName = constructShadowName(groupId);
|
|
66
|
+
async function waitForNodeOnline({ nodeId, groupId, subgroupIds, user, timeoutMs = DEFAULT_NODE_ONLINE_TIMEOUT_MS, pollIntervalMs = DEFAULT_NODE_ONLINE_POLL_INTERVAL_MS, }) {
|
|
67
|
+
const shadowName = constructShadowName(groupId, subgroupIds ?? []);
|
|
68
68
|
logger.info("Waiting for node to come online", {
|
|
69
69
|
nodeId,
|
|
70
70
|
groupId,
|
|
71
|
+
subgroupIds: subgroupIds ?? [],
|
|
71
72
|
shadowName,
|
|
72
73
|
timeoutMs,
|
|
73
74
|
pollIntervalMs,
|
|
@@ -147,10 +148,10 @@ async function waitForNodeOnline({ nodeId, groupId, user, timeoutMs = DEFAULT_NO
|
|
|
147
148
|
});
|
|
148
149
|
await user.connectMQTT();
|
|
149
150
|
}
|
|
150
|
-
// 2.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
// 2. Bind to the membership shadow and subscribe before the shadow may exist.
|
|
152
|
+
// Always registerNode so a leftover longer/stale binding for the same nodeId
|
|
153
|
+
// is rebound to constructShadowName(groupId, subgroupIds).
|
|
154
|
+
NodeMQTTOrchestrator.registerNode(nodeId, shadowName);
|
|
154
155
|
await NodeMQTTOrchestrator.subscribeToNode(nodeId, onUpdate);
|
|
155
156
|
logger.debug("Subscribed to node shadow for online wait", {
|
|
156
157
|
nodeId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waitForNodeOnline.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"waitForNodeOnline.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -9,5 +9,15 @@ export declare class ESPDevice implements ESPDeviceInterface {
|
|
|
9
9
|
name: string;
|
|
10
10
|
transport: string;
|
|
11
11
|
security: number;
|
|
12
|
+
/**
|
|
13
|
+
* What a completed association produced, kept so `retryNetworkCredentials`
|
|
14
|
+
* can resume the flow instead of repeating it. Set once verification
|
|
15
|
+
* succeeds; `undefined` until then.
|
|
16
|
+
*/
|
|
17
|
+
provisionResumeState?: {
|
|
18
|
+
nodeId: string;
|
|
19
|
+
groupId: string;
|
|
20
|
+
options?: Record<string, any>;
|
|
21
|
+
};
|
|
12
22
|
constructor(deviceConfig: ESPDeviceInterface);
|
|
13
23
|
}
|
|
@@ -115,7 +115,7 @@ export declare class ESPRMNeoBase {
|
|
|
115
115
|
static getProvisionAdapter(): ESPProvisionAdapterInterface | undefined;
|
|
116
116
|
/**
|
|
117
117
|
* Sets the local control adapter used to talk to nodes over the LAN via
|
|
118
|
-
* the `
|
|
118
|
+
* the `rmaker_local_ctrl` protocol.
|
|
119
119
|
*
|
|
120
120
|
* @param adapter - Adapter implementing {@link ESPLocalControlAdapterInterface}.
|
|
121
121
|
* @throws {Error} If the SDK is not initialized or the adapter is invalid.
|
|
@@ -22,11 +22,16 @@ export declare class ESPRMNeoNode {
|
|
|
22
22
|
subgroupIds: string[];
|
|
23
23
|
devices: ESPRMNeoDevice[];
|
|
24
24
|
services: ESPRMNeoService[];
|
|
25
|
+
/**
|
|
26
|
+
* Last-known connectivity. Seeded from cached `connectivity_status` when
|
|
27
|
+
* present (cloud config does not provide it); otherwise starts offline until
|
|
28
|
+
* an MQTT shadow `online` update arrives.
|
|
29
|
+
*/
|
|
25
30
|
connectivityStatus: ESPRMNeoConnectivityStatusInterface;
|
|
26
31
|
/**
|
|
27
32
|
* Transports currently usable for this node, keyed by mode.
|
|
28
|
-
* - `mqtt` (cloud) is added when the node
|
|
29
|
-
*
|
|
33
|
+
* - `mqtt` (cloud) is added when the node is connected and removed when it
|
|
34
|
+
* goes offline (from cached `connectivity_status` or shadow updates).
|
|
30
35
|
* - `local` is added/removed at runtime by a `localDiscovery` subscriber as the
|
|
31
36
|
* node appears/disappears on the LAN (via {@link addTransport}/{@link removeTransport}).
|
|
32
37
|
* Apps may also add custom string-keyed entries (pair with
|
|
@@ -132,11 +137,18 @@ export declare class ESPRMNeoNode {
|
|
|
132
137
|
*/
|
|
133
138
|
private processNodeUpdate;
|
|
134
139
|
/**
|
|
135
|
-
* Updates {@link connectivityStatus}
|
|
136
|
-
*
|
|
137
|
-
* an existing status is not cleared.
|
|
140
|
+
* Updates {@link connectivityStatus} when the shadow reports `online`, then
|
|
141
|
+
* syncs MQTT transport availability. Partial updates without `online` are
|
|
142
|
+
* ignored so an existing status is not cleared.
|
|
138
143
|
*/
|
|
139
144
|
private applyConnectivityStatus;
|
|
145
|
+
/**
|
|
146
|
+
* Registers the MQTT transport only while {@link connectivityStatus} reports
|
|
147
|
+
* the node as connected; removes it when offline.
|
|
148
|
+
*/
|
|
149
|
+
private syncMqttTransportAvailability;
|
|
150
|
+
/** Persists {@link wireConfig} (including last-known connectivity) to storage. */
|
|
151
|
+
private persistWireConfig;
|
|
140
152
|
/** When cloud `ncfg_ver` changes, refetch config and persist the new marker. */
|
|
141
153
|
private refreshConfigIfNcfgChanged;
|
|
142
154
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Augments the ESPDevice class with the `resetWifiStatus` method.
|
|
3
|
+
*/
|
|
4
|
+
declare module "../../ESPDevice" {
|
|
5
|
+
interface ESPDevice {
|
|
6
|
+
/**
|
|
7
|
+
* Asks the device to clear its Wi-Fi state over the open provisioning session.
|
|
8
|
+
*
|
|
9
|
+
* Sent on the network-control endpoint, so it needs an established session and
|
|
10
|
+
* leaves the user-node association intact — only Wi-Fi credentials are cleared.
|
|
11
|
+
* That is what makes `retryNetworkCredentials` possible after a wrong password,
|
|
12
|
+
* instead of making the user factory reset the board.
|
|
13
|
+
* @returns A promise resolving to `true` when the device acknowledged the reset.
|
|
14
|
+
*/
|
|
15
|
+
resetWifiStatus(): Promise<boolean>;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ESPProvResponse } from "../../types/provision";
|
|
2
|
+
/**
|
|
3
|
+
* Augments the ESPDevice class with the `retryNetworkCredentials` method.
|
|
4
|
+
*/
|
|
5
|
+
declare module "../../ESPDevice" {
|
|
6
|
+
interface ESPDevice {
|
|
7
|
+
/**
|
|
8
|
+
* Re-sends Wi-Fi credentials after `resetWifiStatus`, resuming the flow the
|
|
9
|
+
* first attempt started rather than repeating its association.
|
|
10
|
+
*
|
|
11
|
+
* Sending credentials alone is not enough: the online wait and the success
|
|
12
|
+
* report follow them and need the node id the association produced, which
|
|
13
|
+
* only this SDK holds.
|
|
14
|
+
* @param ssid - The SSID, unchanged from the first attempt.
|
|
15
|
+
* @param passphrase - The corrected Wi-Fi password.
|
|
16
|
+
* @param onProgress - Progress callback; emits the same messages as `provision`.
|
|
17
|
+
* @returns A promise resolving to the node id.
|
|
18
|
+
* @throws {ESPProvError} When no association has completed on this device yet.
|
|
19
|
+
*/
|
|
20
|
+
retryNetworkCredentials(ssid: string, passphrase: string, onProgress: (message: ESPProvResponse) => void): Promise<string>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -8,6 +8,8 @@ import "./SetProofOfPossession";
|
|
|
8
8
|
import "./SendData";
|
|
9
9
|
import "./InitializeSession";
|
|
10
10
|
import "./SetNetworkCredentials";
|
|
11
|
+
import "./ResetWifiStatus";
|
|
12
|
+
import "./RetryNetworkCredentials";
|
|
11
13
|
import "./InitiateUserNodeMapping";
|
|
12
14
|
import "./VerifyUserNodeMapping";
|
|
13
15
|
import "./StartAssistedClaiming";
|
|
@@ -5,29 +5,39 @@ import { AWSCredentials } from "../../../types/input";
|
|
|
5
5
|
declare module "../../../ESPRMNeoUser" {
|
|
6
6
|
interface ESPRMNeoUser {
|
|
7
7
|
/**
|
|
8
|
-
* Exchanges identity (temporary) credentials for role-based
|
|
8
|
+
* Exchanges identity (temporary) credentials for role-based credentials.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* (
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Two modes — chosen by whether `options.services` is set:
|
|
11
|
+
* 1. Default (no `options.services`): POST /v1/assumed-roles. Returns
|
|
12
|
+
* IoT/MQTT credentials scoped to the user's group/subgroup membership.
|
|
13
|
+
* Used for MQTT WebSocket auth.
|
|
14
|
+
* 2. Per-node services (`options.services` with `nodeId`):
|
|
15
|
+
* Same route, with `services` and `node_id` in the body. Returns
|
|
16
|
+
* credentials scoped to that single node for the listed services
|
|
17
|
+
* (currently `s3` and/or `kvs`). The response contains only the
|
|
18
|
+
* requested service permissions — no IoT/MQTT statements.
|
|
14
19
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* @param accessKey - AWS access key from getTemporaryAWSCredentials (identity credentials).
|
|
20
|
+
* @param accessKey - AWS access key from getTemporaryAWSCredentials.
|
|
18
21
|
* @param secretKey - AWS secret key from getTemporaryAWSCredentials.
|
|
19
22
|
* @param sessionToken - AWS session token from getTemporaryAWSCredentials.
|
|
20
|
-
* @param options - Optional
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @
|
|
24
|
-
* @throws Error if the API call fails or if the response is invalid.
|
|
23
|
+
* @param options - Optional. To request per-node service credentials, pass
|
|
24
|
+
* `services` and `nodeId`.
|
|
25
|
+
* @returns A promise that resolves to AWSCredentials.
|
|
26
|
+
* @throws Error if `services` is set without `nodeId`, or if the API call fails.
|
|
25
27
|
*/
|
|
26
28
|
assumeRole(accessKey: string, secretKey: string, sessionToken: string, options?: AssumeRoleOptions): Promise<AWSCredentials>;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
31
|
+
/** Per-node service credentials supported by the backend. */
|
|
32
|
+
export type AssumeRoleService = "s3" | "kvs";
|
|
29
33
|
/** Options for the assumeRole method. */
|
|
30
34
|
export interface AssumeRoleOptions {
|
|
31
|
-
/**
|
|
32
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Services to scope credentials to (e.g., `["s3"]`, `["kvs"]`, `["s3", "kvs"]`).
|
|
37
|
+
* When set, `nodeId` is required and the returned credentials contain only the
|
|
38
|
+
* listed service permissions for that single node.
|
|
39
|
+
*/
|
|
40
|
+
services?: AssumeRoleService[];
|
|
41
|
+
/** Required when `services` is set. The node these credentials apply to. */
|
|
42
|
+
nodeId?: string;
|
|
33
43
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codec for the `rmaker_local_ctrl` endpoint protocol, mirroring the firmware's
|
|
3
|
+
* `local_ctrl.proto` schema.
|
|
4
|
+
*
|
|
5
|
+
* Hand-rolled like {@link ClaimingProtoHelper} rather than generated: the
|
|
6
|
+
* schema is small, and generated `google-protobuf` modules cannot be pulled
|
|
7
|
+
* into a downstream Metro/RN bundle (see the note in `utils/export.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Field numbers are the wire contract with deployed firmware — treat them as
|
|
10
|
+
* frozen:
|
|
11
|
+
* - `RMakerLocalCtrlPayload`: 1 = msg (varint), 10 = cmdGetData, 11 = respGetData
|
|
12
|
+
* - `CmdGetData`: 1 = DataType (varint), 2 = Offset (varint),
|
|
13
|
+
* 3 = Timestamp (varint), 4 = HasTimestamp (varint)
|
|
14
|
+
* - `RespGetData`: 1 = Status (varint), 2 = Buf
|
|
15
|
+
* - `PayloadBuf`: 1 = Offset (varint), 2 = Payload (bytes), 3 = TotalLen (varint)
|
|
16
|
+
*
|
|
17
|
+
* `Timestamp` / `HasTimestamp` are reserved for a future signed-response
|
|
18
|
+
* extension and are ignored by current firmware, so the encoder omits them.
|
|
19
|
+
*
|
|
20
|
+
* Only `get_params` / `get_config` use this schema; `set_params` carries raw
|
|
21
|
+
* JSON on the wire.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Status returned by the device for a data read.
|
|
25
|
+
*/
|
|
26
|
+
export declare enum RMakerLocalCtrlStatus {
|
|
27
|
+
Success = 0,
|
|
28
|
+
Fail = 1,
|
|
29
|
+
InvalidParam = 2,
|
|
30
|
+
NoMemory = 3
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Selects which document a read targets.
|
|
34
|
+
*/
|
|
35
|
+
export declare enum RMakerLocalCtrlDataType {
|
|
36
|
+
/** The node's params JSON, as served by `get_params`. */
|
|
37
|
+
TypeParams = 0,
|
|
38
|
+
/** The node's config JSON, as served by `get_config`. */
|
|
39
|
+
TypeConfig = 1
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Message type discriminator carried on `RMakerLocalCtrlPayload.msg`.
|
|
43
|
+
*/
|
|
44
|
+
export declare enum RMakerLocalCtrlMsgType {
|
|
45
|
+
TypeCmdGetData = 0,
|
|
46
|
+
TypeRespGetData = 1
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One fragment of a larger document.
|
|
50
|
+
*/
|
|
51
|
+
export interface PayloadBuf {
|
|
52
|
+
/** Byte offset of this fragment within the document. */
|
|
53
|
+
offset: number;
|
|
54
|
+
/** Fragment bytes (up to 200 per response). */
|
|
55
|
+
payload: Uint8Array;
|
|
56
|
+
/** Full document length in bytes. */
|
|
57
|
+
totalLen: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Device response to a data read.
|
|
61
|
+
*/
|
|
62
|
+
export interface RespGetData {
|
|
63
|
+
/** Read status. */
|
|
64
|
+
status: RMakerLocalCtrlStatus;
|
|
65
|
+
/** Fragment carried by this response. */
|
|
66
|
+
buf?: PayloadBuf;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Envelope exchanged on the `get_params` / `get_config` endpoints.
|
|
70
|
+
*/
|
|
71
|
+
export interface RMakerLocalCtrlPayload {
|
|
72
|
+
/** Message type. */
|
|
73
|
+
msg: RMakerLocalCtrlMsgType;
|
|
74
|
+
/** Response payload (device → app). */
|
|
75
|
+
respGetData?: RespGetData;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Encoder/decoder for `rmaker_local_ctrl` frames.
|
|
79
|
+
*/
|
|
80
|
+
export declare class RMakerLocalCtrlProtoHelper {
|
|
81
|
+
/**
|
|
82
|
+
* Builds a `CmdGetData` request for one fragment.
|
|
83
|
+
*
|
|
84
|
+
* An `offset` of 0 makes the device (re)generate and cache the document;
|
|
85
|
+
* subsequent offsets are served from that cache.
|
|
86
|
+
*
|
|
87
|
+
* @param dataType - Document to read.
|
|
88
|
+
* @param offset - Byte offset to read from.
|
|
89
|
+
* @returns The serialized request.
|
|
90
|
+
*/
|
|
91
|
+
static createGetDataRequest(dataType: RMakerLocalCtrlDataType, offset: number): Uint8Array;
|
|
92
|
+
/**
|
|
93
|
+
* Parses a `RespGetData` frame from the device.
|
|
94
|
+
*
|
|
95
|
+
* @param data - Raw response bytes.
|
|
96
|
+
* @returns The parsed payload. Absent fields keep their proto3 defaults.
|
|
97
|
+
*/
|
|
98
|
+
static parseGetDataResponse(data: Uint8Array): RMakerLocalCtrlPayload;
|
|
99
|
+
/**
|
|
100
|
+
* Whether the device reported a successful read.
|
|
101
|
+
*
|
|
102
|
+
* @param response - Parsed response.
|
|
103
|
+
*/
|
|
104
|
+
static isSuccess(response: RMakerLocalCtrlPayload): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Returns the device's status as its enum name, for diagnostics.
|
|
107
|
+
*
|
|
108
|
+
* @param response - Parsed response.
|
|
109
|
+
* @returns The status name, or `"Unknown"` when absent.
|
|
110
|
+
*/
|
|
111
|
+
static getStatus(response: RMakerLocalCtrlPayload): string;
|
|
112
|
+
/**
|
|
113
|
+
* Offset the device answered with, which must match the requested offset.
|
|
114
|
+
*
|
|
115
|
+
* @param response - Parsed response.
|
|
116
|
+
*/
|
|
117
|
+
static getOffset(response: RMakerLocalCtrlPayload): number;
|
|
118
|
+
/**
|
|
119
|
+
* Full document length reported by the device.
|
|
120
|
+
*
|
|
121
|
+
* @param response - Parsed response.
|
|
122
|
+
*/
|
|
123
|
+
static getTotalLen(response: RMakerLocalCtrlPayload): number;
|
|
124
|
+
/**
|
|
125
|
+
* Fragment bytes carried by the response.
|
|
126
|
+
*
|
|
127
|
+
* @param response - Parsed response.
|
|
128
|
+
*/
|
|
129
|
+
static getPayload(response: RMakerLocalCtrlPayload): Uint8Array;
|
|
130
|
+
private static concat;
|
|
131
|
+
private static encodeVarint;
|
|
132
|
+
private static readVarint;
|
|
133
|
+
/** Advances past a field this codec does not read. */
|
|
134
|
+
private static skipField;
|
|
135
|
+
private static parseRespGetData;
|
|
136
|
+
private static parsePayloadBuf;
|
|
137
|
+
}
|
|
@@ -58,6 +58,16 @@ export declare class ESPRMNeoMqtt implements MQTTTransport {
|
|
|
58
58
|
* Returns whether the underlying MQTT client is connected.
|
|
59
59
|
*/
|
|
60
60
|
isConnected(): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Registers a transport connection-status listener when the underlying
|
|
63
|
+
* adapter supports it; otherwise returns a no-op unsubscribe.
|
|
64
|
+
*
|
|
65
|
+
* @param callback - Invoked with `{ connected }` on status changes
|
|
66
|
+
* @returns Unsubscribe function
|
|
67
|
+
*/
|
|
68
|
+
onConnectionStatusChange(callback: (status: {
|
|
69
|
+
connected: boolean;
|
|
70
|
+
}) => void): () => void;
|
|
61
71
|
/**
|
|
62
72
|
* Publish a message to a topic.
|
|
63
73
|
*
|
|
@@ -4,9 +4,10 @@ import { DiscoveryParamsInterface, ESPDiscoveryCallback } from "../../../types/d
|
|
|
4
4
|
* adapter (see {@link ESPRMNeoBase.setLocalDiscoveryAdapter}).
|
|
5
5
|
*
|
|
6
6
|
* - With no discovery config, the default local protocol is used
|
|
7
|
-
* (mDNS service `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* (mDNS service `_esp_rmaker_ctrl._tcp.` in the `local` domain — the
|
|
8
|
+
* service advertised by RainMaker Neo firmware).
|
|
9
|
+
* - A custom {@link DiscoveryParamsInterface} can be supplied to browse any
|
|
10
|
+
* other service type.
|
|
10
11
|
*/
|
|
11
12
|
declare class ESPDiscoveryManager {
|
|
12
13
|
/** Discovery parameters (service type / domain) passed to the adapter. */
|
|
@@ -2,37 +2,84 @@ import type { ESPRMNeoNode } from "../../ESPRMNeoNode";
|
|
|
2
2
|
import { ESPAPIResponse } from "../../types/output";
|
|
3
3
|
import { ESPTransportConfig, ESPTransportInterface } from "../../types/transport";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* the app-supplied {@link ESPRMNeoBase.ESPLocalControlAdapter}
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* The built-in `local` transport, speaking the `rmaker_local_ctrl` endpoint
|
|
6
|
+
* protocol over the app-supplied {@link ESPRMNeoBase.ESPLocalControlAdapter}:
|
|
7
|
+
*
|
|
8
|
+
* - `set_params` carries the same raw JSON body as a cloud set-params call and
|
|
9
|
+
* answers `{"status":"success"}` / `{"status":"fail","description":…}`.
|
|
10
|
+
* - `get_params` / `get_config` exchange protobuf `CmdGetData`/`RespGetData` and
|
|
11
|
+
* are fragmented — the client pulls fixed-size chunks by offset until
|
|
12
|
+
* `TotalLen` is covered.
|
|
13
|
+
*
|
|
14
|
+
* Connection metadata (`baseUrl`, `securityType`, `pop`, and `username` for
|
|
15
|
+
* sec2) is supplied via the transport config by {@link
|
|
16
|
+
* delegatedTransportHandler}; security 0 is not offered by this protocol.
|
|
10
17
|
*/
|
|
11
18
|
declare class ESPLocalControlTransport implements ESPTransportInterface {
|
|
12
|
-
private payload;
|
|
13
19
|
metadata: Record<string, any>;
|
|
14
|
-
propertyInfo: Record<string, any>;
|
|
15
20
|
constructor(transportConfig: ESPTransportConfig);
|
|
16
21
|
private get adapter();
|
|
22
|
+
private ensureConnected;
|
|
17
23
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
24
|
+
* Applies params over `set_params`.
|
|
25
|
+
*
|
|
26
|
+
* @param payload - `{ node_id, payload }`, where `payload` is the
|
|
27
|
+
* `{ <deviceOrServiceName>: { <paramName>: value } }` map to write.
|
|
28
|
+
* @throws {Error} When the device reports a non-success status.
|
|
21
29
|
*/
|
|
22
|
-
private connectWithRetry;
|
|
23
|
-
private ensureConnected;
|
|
24
30
|
setParam(payload: Record<string, any>, _nodeRef?: ESPRMNeoNode): Promise<ESPAPIResponse>;
|
|
31
|
+
/**
|
|
32
|
+
* Reads the node's full params document over `get_params`.
|
|
33
|
+
*
|
|
34
|
+
* @param payload - `{ node_id }` identifying the node to read.
|
|
35
|
+
* @returns The params JSON, keyed by device/service name.
|
|
36
|
+
*/
|
|
25
37
|
getParams(payload: Record<string, any>, _nodeRef?: ESPRMNeoNode): Promise<Record<string, any>>;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Reads the node's config document over `get_config`. Not part of
|
|
40
|
+
* {@link ESPTransportInterface} — the node config normally comes from the
|
|
41
|
+
* cloud; this serves LAN-only flows.
|
|
42
|
+
*
|
|
43
|
+
* @param nodeId - Node to read from.
|
|
44
|
+
* @returns The node config JSON.
|
|
45
|
+
*/
|
|
46
|
+
getConfig(nodeId: string): Promise<Record<string, any>>;
|
|
47
|
+
/**
|
|
48
|
+
* Validates a `set_params` raw-JSON response.
|
|
49
|
+
*
|
|
50
|
+
* @throws {Error} When the body is unparseable or reports a failure.
|
|
51
|
+
*/
|
|
52
|
+
private assertSetParamsAccepted;
|
|
53
|
+
/**
|
|
54
|
+
* Pulls a fragmented document and parses it as JSON, serialized against every
|
|
55
|
+
* other fragmented read of the same node.
|
|
56
|
+
*
|
|
57
|
+
* The device holds **one global transfer cache**, not one per session: an
|
|
58
|
+
* offset-0 request regenerates it and it is freed after the last fragment. So
|
|
59
|
+
* a `getParams()` racing a `getConfig()` on the same node would clobber the
|
|
60
|
+
* other — the second offset-0 regenerates the cache mid-transfer, and the
|
|
61
|
+
* first read's next fragment comes back `Fail` (or, worse, carries bytes from
|
|
62
|
+
* the wrong document). The queue makes that interleaving impossible rather
|
|
63
|
+
* than detecting it after the fact.
|
|
64
|
+
*
|
|
65
|
+
* @param nodeId - Node to read from.
|
|
66
|
+
* @param dataType - Which document to read (params or config).
|
|
67
|
+
* @param endpoint - Endpoint serving that document.
|
|
68
|
+
* @throws {Error} When the device reports a failure, the response is
|
|
69
|
+
* malformed, or a fragment makes no forward progress.
|
|
70
|
+
*/
|
|
71
|
+
private readJsonDocument;
|
|
72
|
+
/** The client-pull loop itself; always reached via {@link readJsonDocument}. */
|
|
73
|
+
private pullJsonDocument;
|
|
74
|
+
private buildGetDataRequest;
|
|
75
|
+
/**
|
|
76
|
+
* Parses one `RespGetData` and checks it answers the requested offset.
|
|
77
|
+
*
|
|
78
|
+
* @param response - Base64 protobuf response from the adapter.
|
|
79
|
+
* @param requestedOffset - Offset asked for, used to detect a desynced pull.
|
|
80
|
+
*/
|
|
81
|
+
private processGetDataResponse;
|
|
82
|
+
/** Joins the pulled fragments and parses the result as JSON. */
|
|
83
|
+
private parseJsonFragments;
|
|
37
84
|
}
|
|
38
85
|
export { ESPLocalControlTransport };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ESPLocalControlAdapterInterface, ESPLocalControlSessionOptions } from "../../types/localControl";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the app-supplied local control adapter.
|
|
4
|
+
*
|
|
5
|
+
* @throws {Error} When no adapter was registered on {@link ESPRMNeoBase}.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getLocalControlAdapter(): ESPLocalControlAdapterInterface;
|
|
8
|
+
/**
|
|
9
|
+
* Ensures a usable local-control session for `nodeId`, connecting (with
|
|
10
|
+
* retries) when the adapter reports none.
|
|
11
|
+
*
|
|
12
|
+
* The adapter resolves on a successful handshake and rejects otherwise, so a
|
|
13
|
+
* resolved `connect` is treated as connected.
|
|
14
|
+
*
|
|
15
|
+
* @param adapter - Adapter to drive.
|
|
16
|
+
* @param nodeId - Node to connect to.
|
|
17
|
+
* @param metadata - Local transport metadata (`baseUrl`, `securityType`, `pop`,
|
|
18
|
+
* `username`).
|
|
19
|
+
* @param options - Session endpoints for the protocol in use; omitted for the
|
|
20
|
+
* adapter's built-in default paths.
|
|
21
|
+
* @param maxRetries - Connection attempts before failing.
|
|
22
|
+
* @throws {Error} When every connection attempt fails.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ensureLocalControlSession(adapter: ESPLocalControlAdapterInterface, nodeId: string, metadata: Record<string, any>, options?: ESPLocalControlSessionOptions, maxRetries?: number): Promise<void>;
|
|
@@ -76,8 +76,8 @@ export declare class NodeMQTTOrchestrator {
|
|
|
76
76
|
/**
|
|
77
77
|
* Registers a node with the orchestrator.
|
|
78
78
|
* Must be called before subscribeToNode, getParams, getShadow, etc.
|
|
79
|
-
* On shadow rename: clears the old binding
|
|
80
|
-
*
|
|
79
|
+
* On shadow rename: clears the old binding, then installs the new name
|
|
80
|
+
* (including shrinks to a shorter membership shadow).
|
|
81
81
|
*
|
|
82
82
|
* @param nodeId - The device/node identifier.
|
|
83
83
|
* @param shadowName - The named shadow (e.g. `params-groupId-subgroupId`).
|