@matter-server/ws-client 1.1.8-alpha.0-20260707-c552b9d → 1.2.1

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/src/client.ts CHANGED
@@ -5,15 +5,17 @@
5
5
  */
6
6
 
7
7
  import { Connection, WebSocketFactory } from "./connection.js";
8
- import { CommandTimeoutError, ConnectionClosedError, InvalidServerVersion } from "./exceptions.js";
8
+ import { CommandTimeoutError, ConnectionClosedError, InvalidServerVersion, ServerCommandError } from "./exceptions.js";
9
9
  import {
10
10
  AccessControlEntry,
11
+ AllCredentialsSummary,
11
12
  APICommands,
12
13
  BindingTarget,
13
14
  CommissionableNodeData,
14
15
  CommissioningParameters,
15
16
  ErrorResultMessage,
16
17
  EventMessage,
18
+ IcdStateData,
17
19
  LogLevelResponse,
18
20
  SettableLogLevelString,
19
21
  MatterFabricData,
@@ -21,6 +23,7 @@ import {
21
23
  MatterSoftwareVersion,
22
24
  NodePingResult,
23
25
  SuccessResultMessage,
26
+ ThreadDiagnosticsBatch,
24
27
  WebRtcCallbackData,
25
28
  } from "./models/model.js";
26
29
  import { MatterNode } from "./models/node.js";
@@ -36,9 +39,33 @@ function toNodeKey(nodeId: number | bigint): string {
36
39
  /** Default timeout for WebSocket commands in milliseconds (5 minutes) */
37
40
  export const DEFAULT_COMMAND_TIMEOUT = 5 * 60 * 1000;
38
41
 
42
+ /** Options for {@link MatterClient.commissionWithCode}. */
43
+ export interface CommissionWithCodeOptions {
44
+ /** Stored WiFi credential id to use (schema 12; omit for the reserved `default` entry). */
45
+ wifiCredentialsId?: string;
46
+ /** Stored Thread dataset id to use (schema 12; omit for the reserved `default` entry). */
47
+ threadDatasetId?: string;
48
+ /** Per-call command timeout in ms (overrides {@link MatterClient.commandTimeout}). */
49
+ timeout?: number;
50
+ }
51
+
52
+ /** Options for the credential set/remove commands. */
53
+ export interface CredentialCommandOptions {
54
+ /** Named credential entry id (schema 12); omit for the reserved `default` entry. */
55
+ id?: string;
56
+ /** Per-call command timeout in ms (overrides {@link MatterClient.commandTimeout}). */
57
+ timeout?: number;
58
+ }
59
+
39
60
  export class MatterClient {
40
61
  public connection: Connection;
41
62
  public nodes: Record<string, MatterNode> = {};
63
+ /**
64
+ * Latest Thread diagnostic batch per network, keyed by 16-char uppercase extPanId hex.
65
+ * Replaced (not mutated) on every update so consumers passing this map as a Lit
66
+ * @property() detect the snapshot change via the default `===` identity compare.
67
+ */
68
+ public threadDiagnostics: ReadonlyMap<string, ThreadDiagnosticsBatch> = new Map();
42
69
  public serverBaseAddress: string;
43
70
  /** Whether this client is connected to a production server (optional, for UI purposes) */
44
71
  public isProduction: boolean = false;
@@ -107,40 +134,91 @@ export class MatterClient {
107
134
  };
108
135
  }
109
136
 
110
- async commissionWithCode(code: string, networkOnly = true, timeout?: number): Promise<MatterNode> {
111
- // Commission a device using a QR Code or Manual Pairing Code.
112
- // code: The QR Code or Manual Pairing Code for device commissioning.
113
- // network_only: If True, restricts device discovery to network only.
114
- // timeout: Optional command timeout in milliseconds.
115
- // Returns: The NodeInfo of the commissioned device.
137
+ commissionWithCode(code: string, networkOnly?: boolean, options?: CommissionWithCodeOptions): Promise<MatterNode>;
138
+ /** @deprecated Pass the timeout via the options object (`{ timeout }`) instead. */
139
+ commissionWithCode(code: string, networkOnly: boolean, timeout: number): Promise<MatterNode>;
140
+ async commissionWithCode(
141
+ code: string,
142
+ networkOnly = true,
143
+ optsOrTimeout?: number | CommissionWithCodeOptions,
144
+ ): Promise<MatterNode> {
145
+ const opts = typeof optsOrTimeout === "number" ? { timeout: optsOrTimeout } : (optsOrTimeout ?? {});
146
+ const usesIds = opts.wifiCredentialsId !== undefined || opts.threadDatasetId !== undefined;
116
147
  const data = await this.sendCommand(
117
148
  "commission_with_code",
118
- 0,
149
+ usesIds ? 12 : 0,
119
150
  {
120
- code: code,
151
+ code,
121
152
  network_only: networkOnly,
153
+ wifi_credentials_id: opts.wifiCredentialsId,
154
+ thread_dataset_id: opts.threadDatasetId,
122
155
  },
123
- timeout,
156
+ opts.timeout,
124
157
  );
125
158
  return new MatterNode(data);
126
159
  }
127
160
 
128
- async setWifiCredentials(ssid: string, credentials: string, timeout?: number): Promise<void> {
129
- // Set WiFi credentials for commissioning to a (new) device.
130
- await this.sendCommand("set_wifi_credentials", 0, { ssid, credentials }, timeout);
161
+ setWifiCredentials(ssid: string, credentials: string, options?: CredentialCommandOptions): Promise<void>;
162
+ /** @deprecated Pass the timeout via the options object (`{ timeout }`) instead. */
163
+ setWifiCredentials(ssid: string, credentials: string, timeout: number): Promise<void>;
164
+ async setWifiCredentials(
165
+ ssid: string,
166
+ credentials: string,
167
+ optsOrTimeout?: number | CredentialCommandOptions,
168
+ ): Promise<void> {
169
+ const opts = typeof optsOrTimeout === "number" ? { timeout: optsOrTimeout } : (optsOrTimeout ?? {});
170
+ await this.sendCommand(
171
+ "set_wifi_credentials",
172
+ opts.id === undefined || opts.id === "default" ? 0 : 12,
173
+ { ssid, credentials, id: opts.id },
174
+ opts.timeout,
175
+ );
131
176
  }
132
177
 
133
- async setThreadOperationalDataset(dataset: string, timeout?: number): Promise<void> {
134
- // Set Thread Operational dataset in the stack.
135
- await this.sendCommand("set_thread_dataset", 0, { dataset }, timeout);
178
+ setThreadOperationalDataset(dataset: string, options?: CredentialCommandOptions): Promise<void>;
179
+ /** @deprecated Pass the timeout via the options object (`{ timeout }`) instead. */
180
+ setThreadOperationalDataset(dataset: string, timeout: number): Promise<void>;
181
+ async setThreadOperationalDataset(
182
+ dataset: string,
183
+ optsOrTimeout?: number | CredentialCommandOptions,
184
+ ): Promise<void> {
185
+ const opts = typeof optsOrTimeout === "number" ? { timeout: optsOrTimeout } : (optsOrTimeout ?? {});
186
+ await this.sendCommand(
187
+ "set_thread_dataset",
188
+ opts.id === undefined || opts.id === "default" ? 0 : 12,
189
+ { dataset, id: opts.id },
190
+ opts.timeout,
191
+ );
136
192
  }
137
193
 
138
- async removeWifiCredentials(timeout?: number): Promise<void> {
139
- await this.sendCommand("remove_wifi_credentials", 0, {}, timeout);
194
+ removeWifiCredentials(options?: CredentialCommandOptions): Promise<void>;
195
+ /** @deprecated Pass the timeout via the options object (`{ timeout }`) instead. */
196
+ removeWifiCredentials(timeout: number): Promise<void>;
197
+ async removeWifiCredentials(optsOrTimeout?: number | CredentialCommandOptions): Promise<void> {
198
+ const opts = typeof optsOrTimeout === "number" ? { timeout: optsOrTimeout } : (optsOrTimeout ?? {});
199
+ await this.sendCommand(
200
+ "remove_wifi_credentials",
201
+ opts.id === undefined || opts.id === "default" ? 0 : 12,
202
+ { id: opts.id },
203
+ opts.timeout,
204
+ );
205
+ }
206
+
207
+ removeThreadDataset(options?: CredentialCommandOptions): Promise<void>;
208
+ /** @deprecated Pass the timeout via the options object (`{ timeout }`) instead. */
209
+ removeThreadDataset(timeout: number): Promise<void>;
210
+ async removeThreadDataset(optsOrTimeout?: number | CredentialCommandOptions): Promise<void> {
211
+ const opts = typeof optsOrTimeout === "number" ? { timeout: optsOrTimeout } : (optsOrTimeout ?? {});
212
+ await this.sendCommand(
213
+ "remove_thread_dataset",
214
+ opts.id === undefined || opts.id === "default" ? 0 : 12,
215
+ { id: opts.id },
216
+ opts.timeout,
217
+ );
140
218
  }
141
219
 
142
- async removeThreadDataset(timeout?: number): Promise<void> {
143
- await this.sendCommand("remove_thread_dataset", 0, {}, timeout);
220
+ async getAllCredentials(timeout?: number): Promise<AllCredentialsSummary> {
221
+ return this.sendCommand("get_all_credentials", 12, {}, timeout);
144
222
  }
145
223
 
146
224
  async openCommissioningWindow(
@@ -219,6 +297,35 @@ export class MatterClient {
219
297
  await this.sendCommand("interview_node", 0, { node_id: nodeId }, timeout);
220
298
  }
221
299
 
300
+ async registerIcd(
301
+ nodeId: number | bigint,
302
+ options?: { allowMultiAdmin?: boolean; ignoredVendors?: number[] },
303
+ timeout?: number,
304
+ ): Promise<IcdStateData> {
305
+ return this.sendCommand(
306
+ "register_icd",
307
+ 0,
308
+ {
309
+ node_id: nodeId,
310
+ allow_multi_admin: options?.allowMultiAdmin,
311
+ ignored_vendors: options?.ignoredVendors,
312
+ },
313
+ timeout,
314
+ );
315
+ }
316
+
317
+ async unregisterIcd(nodeId: number | bigint, force?: boolean, timeout?: number): Promise<IcdStateData> {
318
+ return this.sendCommand("unregister_icd", 0, { node_id: nodeId, force }, timeout);
319
+ }
320
+
321
+ async resyncIcd(nodeId: number | bigint): Promise<null> {
322
+ return this.sendCommand("resync_icd", 0, { node_id: nodeId });
323
+ }
324
+
325
+ async getIcdState(nodeId: number | bigint, timeout?: number): Promise<IcdStateData> {
326
+ return this.sendCommand("get_icd_state", 0, { node_id: nodeId }, timeout);
327
+ }
328
+
222
329
  async importTestNode(dump: string, timeout?: number): Promise<void> {
223
330
  // Import test node(s) from a HA or Matter server diagnostics dump.
224
331
  await this.sendCommand("import_test_node", 0, { dump }, timeout);
@@ -354,6 +461,11 @@ export class MatterClient {
354
461
  await this.sendCommand("set_default_fabric_label", 0, { label }, timeout);
355
462
  }
356
463
 
464
+ async getFabricLabel(timeout?: number): Promise<string | null> {
465
+ const { fabric_label } = await this.sendCommand("get_fabric_label", 12, {}, timeout);
466
+ return fabric_label;
467
+ }
468
+
357
469
  /**
358
470
  * Get the current log levels for console and file logging.
359
471
  * @param timeout Optional command timeout in milliseconds
@@ -529,6 +641,10 @@ export class MatterClient {
529
641
  nodes[toNodeKey(node.node_id)] = new MatterNode(node);
530
642
  }
531
643
  this.nodes = nodes;
644
+
645
+ // Reset diagnostics on every (re)start so a server restart doesn't leave batches for networks
646
+ // the server no longer knows; fresh batches arrive via the thread_diagnostics_updated event.
647
+ this.threadDiagnostics = new Map();
532
648
  }
533
649
 
534
650
  private _handleIncomingMessage(msg: IncomingMessage) {
@@ -538,7 +654,11 @@ export class MatterClient {
538
654
  }
539
655
 
540
656
  if ("error_code" in msg) {
541
- this._rejectPendingCommand(msg.message_id, new Error(msg.details));
657
+ const details = msg.details ?? "";
658
+ this._rejectPendingCommand(
659
+ msg.message_id,
660
+ new ServerCommandError(details !== "" ? details : `Server error ${msg.error_code}`, msg.error_code),
661
+ );
542
662
  return;
543
663
  }
544
664
 
@@ -601,6 +721,14 @@ export class MatterClient {
601
721
  return;
602
722
  }
603
723
 
724
+ if (event.event === "thread_diagnostics_updated") {
725
+ const next = new Map(this.threadDiagnostics);
726
+ next.set(event.data.extPanIdHex.toUpperCase(), event.data);
727
+ this.threadDiagnostics = next;
728
+ this.fireEvent("thread_diagnostics_updated");
729
+ return;
730
+ }
731
+
604
732
  if (event.event === "webrtc_callback") {
605
733
  for (const listener of this.webrtcCallbackListeners) {
606
734
  listener(event.data);
package/src/exceptions.ts CHANGED
@@ -30,3 +30,14 @@ export class ConnectionClosedError extends MatterError {
30
30
  this.name = "ConnectionClosedError";
31
31
  }
32
32
  }
33
+
34
+ /** Command failure reported by the server; preserves the wire error_code. */
35
+ export class ServerCommandError extends MatterError {
36
+ constructor(
37
+ message: string,
38
+ readonly errorCode: number,
39
+ ) {
40
+ super(message);
41
+ this.name = "ServerCommandError";
42
+ }
43
+ }
package/src/index.ts CHANGED
@@ -23,3 +23,7 @@ export * from "./json-utils.js";
23
23
  // Export models
24
24
  export * from "./models/model.js";
25
25
  export * from "./models/node.js";
26
+
27
+ // Export network topology derivation (shared by dashboard + server)
28
+ export * from "./topology/topology-types.js";
29
+ export * from "./topology/topology-utils.js";