@espressif/rainmaker-neo-base-sdk 1.1.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.
Files changed (55) hide show
  1. package/dist/cjs/ESPRMNeoBase.js +1 -1
  2. package/dist/cjs/index.js +11 -0
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/proto/rmaker_local_ctrl.js +273 -0
  5. package/dist/cjs/proto/rmaker_local_ctrl.js.map +1 -0
  6. package/dist/cjs/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js +5 -4
  7. package/dist/cjs/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js.map +1 -1
  8. package/dist/cjs/services/ESPTransport/ESPLocalControlTransport.js +183 -116
  9. package/dist/cjs/services/ESPTransport/ESPLocalControlTransport.js.map +1 -1
  10. package/dist/cjs/services/ESPTransport/LocalControlSession.js +62 -0
  11. package/dist/cjs/services/ESPTransport/LocalControlSession.js.map +1 -0
  12. package/dist/cjs/types/transport.js +24 -0
  13. package/dist/cjs/types/transport.js.map +1 -1
  14. package/dist/cjs/utils/constants.js +70 -4
  15. package/dist/cjs/utils/constants.js.map +1 -1
  16. package/dist/cjs/utils/eventSubscriptionUtils.js +48 -4
  17. package/dist/cjs/utils/eventSubscriptionUtils.js.map +1 -1
  18. package/dist/esm/ESPRMNeoBase.js +1 -1
  19. package/dist/esm/index.js +2 -2
  20. package/dist/esm/proto/rmaker_local_ctrl.js +271 -0
  21. package/dist/esm/proto/rmaker_local_ctrl.js.map +1 -0
  22. package/dist/esm/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js +5 -4
  23. package/dist/esm/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.js.map +1 -1
  24. package/dist/esm/services/ESPTransport/ESPLocalControlTransport.js +184 -117
  25. package/dist/esm/services/ESPTransport/ESPLocalControlTransport.js.map +1 -1
  26. package/dist/esm/services/ESPTransport/LocalControlSession.js +59 -0
  27. package/dist/esm/services/ESPTransport/LocalControlSession.js.map +1 -0
  28. package/dist/esm/types/transport.js +24 -1
  29. package/dist/esm/types/transport.js.map +1 -1
  30. package/dist/esm/utils/constants.js +65 -5
  31. package/dist/esm/utils/constants.js.map +1 -1
  32. package/dist/esm/utils/eventSubscriptionUtils.js +49 -5
  33. package/dist/esm/utils/eventSubscriptionUtils.js.map +1 -1
  34. package/dist/types/ESPRMNeoBase.d.ts +1 -1
  35. package/dist/types/proto/rmaker_local_ctrl.d.ts +137 -0
  36. package/dist/types/services/ESPTransport/ESPDiscovery/ESPDiscoveryManager.d.ts +4 -3
  37. package/dist/types/services/ESPTransport/ESPLocalControlTransport.d.ts +70 -23
  38. package/dist/types/services/ESPTransport/LocalControlSession.d.ts +24 -0
  39. package/dist/types/types/discovery.d.ts +1 -1
  40. package/dist/types/types/localControl.d.ts +26 -1
  41. package/dist/types/types/transport.d.ts +24 -2
  42. package/dist/types/utils/baseUtils.d.ts +1 -1
  43. package/dist/types/utils/constants.d.ts +65 -5
  44. package/dist/types/utils/eventSubscriptionUtils.d.ts +19 -3
  45. package/package.json +1 -1
  46. package/dist/cjs/proto/constants.js +0 -24
  47. package/dist/cjs/proto/constants.js.map +0 -1
  48. package/dist/cjs/proto/esp_local_ctrl.js +0 -787
  49. package/dist/cjs/proto/esp_local_ctrl.js.map +0 -1
  50. package/dist/esm/proto/constants.js +0 -24
  51. package/dist/esm/proto/constants.js.map +0 -1
  52. package/dist/esm/proto/esp_local_ctrl.js +0 -758
  53. package/dist/esm/proto/esp_local_ctrl.js.map +0 -1
  54. package/dist/types/proto/constants.d.ts +0 -15
  55. package/dist/types/proto/esp_local_ctrl.d.ts +0 -283
@@ -1 +1 @@
1
- {"version":3,"file":"ESPLocalControlTransport.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"ESPLocalControlTransport.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,59 @@
1
+ import { ESPRMNeoBase } from '../../ESPRMNeoBase.js';
2
+
3
+ /*
4
+ * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
5
+ *
6
+ * SPDX-License-Identifier: Apache-2.0
7
+ */
8
+ /** Attempts made before a local-control handshake is reported as failed. */
9
+ const DEFAULT_CONNECT_RETRIES = 3;
10
+ /**
11
+ * Returns the app-supplied local control adapter.
12
+ *
13
+ * @throws {Error} When no adapter was registered on {@link ESPRMNeoBase}.
14
+ */
15
+ function getLocalControlAdapter() {
16
+ const adapter = ESPRMNeoBase.getLocalControlAdapter();
17
+ if (!adapter) {
18
+ throw new Error("Local control adapter is not configured");
19
+ }
20
+ return adapter;
21
+ }
22
+ /**
23
+ * Ensures a usable local-control session for `nodeId`, connecting (with
24
+ * retries) when the adapter reports none.
25
+ *
26
+ * The adapter resolves on a successful handshake and rejects otherwise, so a
27
+ * resolved `connect` is treated as connected.
28
+ *
29
+ * @param adapter - Adapter to drive.
30
+ * @param nodeId - Node to connect to.
31
+ * @param metadata - Local transport metadata (`baseUrl`, `securityType`, `pop`,
32
+ * `username`).
33
+ * @param options - Session endpoints for the protocol in use; omitted for the
34
+ * adapter's built-in default paths.
35
+ * @param maxRetries - Connection attempts before failing.
36
+ * @throws {Error} When every connection attempt fails.
37
+ */
38
+ async function ensureLocalControlSession(adapter, nodeId, metadata, options, maxRetries = DEFAULT_CONNECT_RETRIES) {
39
+ if (await adapter.isConnected(nodeId)) {
40
+ return;
41
+ }
42
+ let attempt = 0;
43
+ let lastError;
44
+ while (attempt < maxRetries) {
45
+ try {
46
+ await adapter.connect(nodeId, metadata.baseUrl, metadata.securityType ?? 0, metadata.pop, metadata.username, options);
47
+ return;
48
+ }
49
+ catch (error) {
50
+ lastError = error;
51
+ attempt += 1;
52
+ }
53
+ }
54
+ const message = lastError instanceof Error ? lastError.message : String(lastError);
55
+ throw new Error(`Failed to connect after ${maxRetries} attempts: ${message}`);
56
+ }
57
+
58
+ export { ensureLocalControlSession, getLocalControlAdapter };
59
+ //# sourceMappingURL=LocalControlSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalControlSession.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -17,6 +17,29 @@ var ESPTransportMode;
17
17
  /** Communication is routed through MQTT (RainMaker Neo cloud: AWS shadow). */
18
18
  ESPTransportMode["mqtt"] = "mqtt";
19
19
  })(ESPTransportMode || (ESPTransportMode = {}));
20
+ /**
21
+ * Wire protocol spoken by the `local` transport. Both protocols run over a
22
+ * protocomm session on the node's LAN HTTP server; they differ in endpoint
23
+ * names and message encoding.
24
+ *
25
+ * Carried on the local transport config's `metadata.protocol`, set by local
26
+ * discovery from the mDNS service type that produced the hit.
27
+ */
28
+ var ESPLocalControlProtocol;
29
+ (function (ESPLocalControlProtocol) {
30
+ /**
31
+ * RainMaker Neo protocol (`rmaker_local_ctrl/*` session plus
32
+ * `get_params`/`set_params`/`get_config`), advertised as
33
+ * `_esp_rmaker_ctrl._tcp`. Default for this SDK.
34
+ */
35
+ ESPLocalControlProtocol["rmakerLocalCtrl"] = "rmaker_local_ctrl";
36
+ })(ESPLocalControlProtocol || (ESPLocalControlProtocol = {}));
37
+ /**
38
+ * Protocol assumed when a local transport config carries no explicit
39
+ * `metadata.protocol` — for example a LAN transport restored from a client-side
40
+ * registry rather than a fresh discovery hit.
41
+ */
42
+ const DEFAULT_LOCAL_CONTROL_PROTOCOL = ESPLocalControlProtocol.rmakerLocalCtrl;
20
43
  /**
21
44
  * Default transport priority order used when neither the node nor the SDK base
22
45
  * has an explicit order configured: local control first, MQTT (cloud) fallback.
@@ -26,5 +49,5 @@ const DEFAULT_TRANSPORT_ORDER = [
26
49
  ESPTransportMode.mqtt,
27
50
  ];
28
51
 
29
- export { DEFAULT_TRANSPORT_ORDER, ESPTransportMode };
52
+ export { DEFAULT_LOCAL_CONTROL_PROTOCOL, DEFAULT_TRANSPORT_ORDER, ESPLocalControlProtocol, ESPTransportMode };
30
53
  //# sourceMappingURL=transport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"transport.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -473,8 +473,6 @@ const ProvisionType = {
473
473
  * An object containing endpoint paths.
474
474
  */
475
475
  const Endpoint = {
476
- /** The endpoint for local control. */
477
- LOCAL_CTRL: "esp_local_ctrl/control",
478
476
  /** The endpoint for cloud user association. */
479
477
  CLOUD_USER_ASSOCIATION: "cloud_user_assoc",
480
478
  /** The endpoint for challenge-response and get-node-id protocol with the device. */
@@ -482,6 +480,64 @@ const Endpoint = {
482
480
  /** The endpoint for assisted claiming. */
483
481
  RM_CLAIM: "rmaker_claim",
484
482
  };
483
+ /**
484
+ * Endpoint paths of the `rmaker_local_ctrl` protocol served by RainMaker Neo
485
+ * firmware (see the firmware's local-control endpoint protocol spec).
486
+ *
487
+ * All data endpoints inherit the security of the session established on
488
+ * {@link RMakerLocalCtrlEndpoint.SESSION}.
489
+ */
490
+ const RMakerLocalCtrlEndpoint = {
491
+ /** Protocomm session-security endpoint (SEC1 with/without PoP, or SEC2). */
492
+ SESSION: "rmaker_local_ctrl/session",
493
+ /** POST any payload; responds with the service info JSON (`sec_ver`, `cap`, …). */
494
+ VERSION: "rmaker_local_ctrl/version",
495
+ /** Reads the params JSON — protobuf `CmdGetData`, fragmented response. */
496
+ GET_PARAMS: "get_params",
497
+ /** Reads the node config JSON — protobuf `CmdGetData`, fragmented response. */
498
+ GET_CONFIG: "get_config",
499
+ /** Writes params — raw JSON request and response (no protobuf). */
500
+ SET_PARAMS: "set_params",
501
+ };
502
+ /**
503
+ * Root key of the JSON served by {@link RMakerLocalCtrlEndpoint.VERSION}, i.e.
504
+ * `{"rmaker_local_ctrl": {"ver": …, "sec_ver": …, "cap": [...]}}`. Passed to the
505
+ * local-control adapter so the native layer probes the right scheme version.
506
+ */
507
+ const RMAKER_LOCAL_CTRL_VERSION_KEY = "rmaker_local_ctrl";
508
+ /**
509
+ * Maximum fragment size (bytes) the firmware serves per `RespGetData`. Reads
510
+ * loop with `Offset += Payload.length` until `TotalLen` is reached; this value
511
+ * is informational (the device dictates the actual fragment length).
512
+ */
513
+ const RMAKER_LOCAL_CTRL_FRAGMENT_SIZE = 200;
514
+ /**
515
+ * `status` values in a `set_params` raw-JSON response.
516
+ */
517
+ const RMakerLocalCtrlSetParamsStatus = {
518
+ SUCCESS: "success",
519
+ FAIL: "fail",
520
+ };
521
+ /**
522
+ * mDNS TXT record keys advertised by the `_esp_rmaker_ctrl._tcp` service.
523
+ */
524
+ const RMakerLocalCtrlTxtKey = {
525
+ /** Node ID (also the service instance name and hostname). */
526
+ NODE_ID: "node_id",
527
+ /** Comma-separated active capabilities — see {@link RMakerLocalCtrlCapability}. */
528
+ CAP: "cap",
529
+ };
530
+ /**
531
+ * Capability tokens found in the `cap` TXT record. A node advertising only
532
+ * `ch_resp` is reachable for on-network user-node association but *not* for
533
+ * param control, so it must not be registered as a local control transport.
534
+ */
535
+ const RMakerLocalCtrlCapability = {
536
+ /** Params/config endpoints are registered. */
537
+ LOCAL_CTRL: "local_ctrl",
538
+ /** Challenge-response (on-network user-node association) is registered. */
539
+ CH_RESP: "ch_resp",
540
+ };
485
541
  /**
486
542
  * Assisted-claiming REST paths, on the deployment's main API and SigV4-signed
487
543
  * like every other `/v1/*` route.
@@ -639,8 +695,12 @@ const ProvErrorCodes = {
639
695
  * @enum {string}
640
696
  */
641
697
  const ServiceType = {
642
- /** Represents the ESP local control TCP service type. */
643
- ESP_LOCAL_CTRL_TCP: "_esp_local_ctrl._tcp.",
698
+ /**
699
+ * Service type advertised by RainMaker Neo firmware. A single instance serves
700
+ * the `rmaker_local_ctrl` endpoints; the `cap` TXT record says which endpoint
701
+ * sets are active. This is the default for local discovery in this SDK.
702
+ */
703
+ ESP_RMAKER_LOCAL_CTRL_TCP: "_esp_rmaker_ctrl._tcp.",
644
704
  };
645
705
  /**
646
706
  * An object containing protocol types.
@@ -849,5 +909,5 @@ const SubscriptionChannelIds = {
849
909
  MQTT: "mqtt",
850
910
  };
851
911
 
852
- export { APICallValidationErrorCodes, APIEndpoints, APIOperations, APIPathV1, AWSCredentialsErrorMessages, AssumeRoleErrorMessages, AuthErrorCodes, AuthSuccessMessages, AutomationStatusValues, AutomationSuccessMessages, CLAIM_CSR_MAX_CHUNKS, CLAIM_DEVICE_CSR_KEYS, CLAIM_DEVICE_MAC_KEYS, CLAIM_MAC_HEX_LENGTHS, CLAIM_MAC_SEPARATORS, CLAIM_MAX_FRAGMENT_SIZE, ClaimCapabilities, ClaimCapabilityPolicies, ClaimEndpoints, ClaimErrorCodes, ClaimProgressMessages, ConfigErrorCodes, DEFAULT_REST_API_VERSION, ESPProvProgressMessages, ESPRMNeoErrorCodes, ESPRMNeoStorageKeys, ESPServiceParamType, ESPServiceType, Endpoint, ErrorLabels, GroupSuccessMessages, GroupUserAliases, HTTPMethods, IntegrationSuccessMessages, NodeSuccessMessages, NodeWarnMessages, ProtocolType, ProvErrorCodes, ProvisionType, SDK_VERSION, ScheduleErrorMessages, ScheduleSuccessMessages, ServiceType, SharingSuccessMessages, StatusMessage, StorageAdapterErrorCodes, StorageKeys, SubscriptionChannelIds, TokenErrorCodes, TriggerErrorMessages, TriggerSuccessMessages, ValidationErrorCodes };
912
+ export { APICallValidationErrorCodes, APIEndpoints, APIOperations, APIPathV1, AWSCredentialsErrorMessages, AssumeRoleErrorMessages, AuthErrorCodes, AuthSuccessMessages, AutomationStatusValues, AutomationSuccessMessages, CLAIM_CSR_MAX_CHUNKS, CLAIM_DEVICE_CSR_KEYS, CLAIM_DEVICE_MAC_KEYS, CLAIM_MAC_HEX_LENGTHS, CLAIM_MAC_SEPARATORS, CLAIM_MAX_FRAGMENT_SIZE, ClaimCapabilities, ClaimCapabilityPolicies, ClaimEndpoints, ClaimErrorCodes, ClaimProgressMessages, ConfigErrorCodes, DEFAULT_REST_API_VERSION, ESPProvProgressMessages, ESPRMNeoErrorCodes, ESPRMNeoStorageKeys, ESPServiceParamType, ESPServiceType, Endpoint, ErrorLabels, GroupSuccessMessages, GroupUserAliases, HTTPMethods, IntegrationSuccessMessages, NodeSuccessMessages, NodeWarnMessages, ProtocolType, ProvErrorCodes, ProvisionType, RMAKER_LOCAL_CTRL_FRAGMENT_SIZE, RMAKER_LOCAL_CTRL_VERSION_KEY, RMakerLocalCtrlCapability, RMakerLocalCtrlEndpoint, RMakerLocalCtrlSetParamsStatus, RMakerLocalCtrlTxtKey, SDK_VERSION, ScheduleErrorMessages, ScheduleSuccessMessages, ServiceType, SharingSuccessMessages, StatusMessage, StorageAdapterErrorCodes, StorageKeys, SubscriptionChannelIds, TokenErrorCodes, TriggerErrorMessages, TriggerSuccessMessages, ValidationErrorCodes };
853
913
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"constants.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -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
- /** Maps a default local-discovery hit into the client-facing payload shape. */
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: { baseUrl: info.baseUrl },
48
+ metadata: {
49
+ baseUrl: info.baseUrl,
50
+ protocol: ESPLocalControlProtocol.rmakerLocalCtrl,
51
+ ...(capabilities && { capabilities }),
52
+ },
17
53
  },
18
54
  };
19
55
  }
20
- /** Starts LAN discovery; each hit is mapped then passed to `onDiscovered`. */
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) => onDiscovered(toDiscoveredNodeData(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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -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 `esp_local_ctrl` protocol.
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.
@@ -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
+ }
@@ -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 `_esp_local_ctrl._tcp.` in the `local` domain).
8
- * - A custom {@link DiscoveryParamsInterface} can be supplied for other
9
- * discovery protocols.
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
- * Built-in `local` transport. Communicates with the node over the LAN through
6
- * the app-supplied {@link ESPRMNeoBase.ESPLocalControlAdapter} using the
7
- * `esp_local_ctrl` protobuf protocol. Connection metadata (`baseUrl`,
8
- * `securityType`, `pop`, and `username` for sec2) is supplied via the transport
9
- * config by {@link delegatedTransportHandler}.
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
- * Connects to the node, retrying on failure. RMNeo's adapter resolves on a
19
- * successful connection and rejects otherwise, so a resolved call is treated
20
- * as connected.
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
- private setProperty;
27
- private buildSetPropertyRequest;
28
- private processSetPropertyResponse;
29
- /** Fetches the property count, then reads each property value into propertyInfo. */
30
- private getPropertyInfo;
31
- private fetchPropertyCount;
32
- private buildGetPropertyCountRequest;
33
- private processGetPropertyCountResponse;
34
- private fetchPropertyValue;
35
- private buildGetPropertyValueRequest;
36
- private processGetPropertyValueResponse;
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>;
@@ -23,7 +23,7 @@ declare enum ESPRMNeoEventType {
23
23
  * (e.g. mDNS service type and domain).
24
24
  */
25
25
  interface DiscoveryParamsInterface {
26
- /** Service type to browse for, e.g. `_esp_local_ctrl._tcp.` */
26
+ /** Service type to browse for, e.g. `_esp_rmaker_ctrl._tcp.` */
27
27
  serviceType: string;
28
28
  /** Discovery domain, e.g. `local`. */
29
29
  domain: string;
@@ -1,3 +1,25 @@
1
+ /**
2
+ * Per-connection session details passed to {@link
3
+ * ESPLocalControlAdapterInterface.connect}. The protocomm handshake endpoint
4
+ * differs per local-control protocol, so the transport tells the native layer
5
+ * which paths to use instead of the adapter hardcoding them.
6
+ *
7
+ * Adapters built against an earlier SDK simply ignore the extra argument and
8
+ * keep using their built-in default paths.
9
+ */
10
+ export interface ESPLocalControlSessionOptions {
11
+ /** Protocol tag, one of {@link ESPLocalControlProtocol}. */
12
+ protocol: string;
13
+ /** Protocomm session-security endpoint, e.g. `rmaker_local_ctrl/session`. */
14
+ sessionPath: string;
15
+ /** Version/service-info endpoint, e.g. `rmaker_local_ctrl/version`. */
16
+ versionPath: string;
17
+ /**
18
+ * Root key to read in the version response when probing the security scheme
19
+ * version, e.g. `rmaker_local_ctrl` for `{"rmaker_local_ctrl": {…}}`.
20
+ */
21
+ versionKey: string;
22
+ }
1
23
  /**
2
24
  * Local control adapter interface for node communication over LAN.
3
25
  */
@@ -8,8 +30,11 @@ export interface ESPLocalControlAdapterInterface {
8
30
  isConnected(nodeId: string): Promise<boolean>;
9
31
  /**
10
32
  * Connects to the node with local control parameters.
33
+ *
34
+ * @param options - Session endpoints for the protocol in use. Omitted only by
35
+ * callers that want the adapter's built-in default paths.
11
36
  */
12
- connect(nodeId: string, baseUrl: string, securtiyType: number, pop?: string, username?: string): Promise<Record<string, unknown>>;
37
+ connect(nodeId: string, baseUrl: string, securtiyType: number, pop?: string, username?: string, options?: ESPLocalControlSessionOptions): Promise<Record<string, unknown>>;
13
38
  /**
14
39
  * Sends data to the specified path on the node.
15
40
  */