@matter-server/dashboard 0.2.7-alpha.0-20260118-993a1c7 → 0.2.7-alpha.0-20260119-49e7237

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 (80) hide show
  1. package/dist/esm/components/dialogs/binding/node-binding-dialog.d.ts.map +1 -1
  2. package/dist/esm/components/dialogs/binding/node-binding-dialog.js +3 -2
  3. package/dist/esm/components/dialogs/binding/node-binding-dialog.js.map +1 -1
  4. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.d.ts.map +1 -1
  5. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.js +2 -1
  6. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.js.map +1 -1
  7. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.d.ts.map +1 -1
  8. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.js +3 -2
  9. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.js.map +1 -1
  10. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.d.ts.map +1 -1
  11. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.js +5 -4
  12. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.js.map +1 -1
  13. package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts +33 -0
  14. package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts.map +1 -0
  15. package/dist/esm/components/dialogs/settings/log-level-dialog.js +189 -0
  16. package/dist/esm/components/dialogs/settings/log-level-dialog.js.map +6 -0
  17. package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts +8 -0
  18. package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts.map +1 -0
  19. package/dist/esm/components/dialogs/settings/show-log-level-dialog.js +15 -0
  20. package/dist/esm/components/dialogs/settings/show-log-level-dialog.js.map +6 -0
  21. package/dist/esm/pages/cluster-commands/clusters/level-control-commands.d.ts.map +1 -1
  22. package/dist/esm/pages/cluster-commands/clusters/level-control-commands.js +5 -2
  23. package/dist/esm/pages/cluster-commands/clusters/level-control-commands.js.map +1 -1
  24. package/dist/esm/pages/cluster-commands/clusters/on-off-commands.d.ts.map +1 -1
  25. package/dist/esm/pages/cluster-commands/clusters/on-off-commands.js +6 -3
  26. package/dist/esm/pages/cluster-commands/clusters/on-off-commands.js.map +1 -1
  27. package/dist/esm/pages/components/header.d.ts +1 -0
  28. package/dist/esm/pages/components/header.d.ts.map +1 -1
  29. package/dist/esm/pages/components/header.js +13 -1
  30. package/dist/esm/pages/components/header.js.map +1 -1
  31. package/dist/esm/pages/components/node-details.d.ts.map +1 -1
  32. package/dist/esm/pages/components/node-details.js +6 -5
  33. package/dist/esm/pages/components/node-details.js.map +1 -1
  34. package/dist/esm/pages/components/server-details.d.ts.map +1 -1
  35. package/dist/esm/pages/components/server-details.js +3 -2
  36. package/dist/esm/pages/components/server-details.js.map +1 -1
  37. package/dist/esm/pages/matter-server-view.d.ts.map +1 -1
  38. package/dist/esm/pages/matter-server-view.js +14 -1
  39. package/dist/esm/pages/matter-server-view.js.map +1 -1
  40. package/dist/esm/util/async-handler.d.ts +50 -0
  41. package/dist/esm/util/async-handler.d.ts.map +1 -0
  42. package/dist/esm/util/async-handler.js +33 -0
  43. package/dist/esm/util/async-handler.js.map +6 -0
  44. package/dist/esm/util/fire_event.d.ts.map +1 -1
  45. package/dist/esm/util/fire_event.js +1 -2
  46. package/dist/esm/util/fire_event.js.map +1 -1
  47. package/dist/esm/util/format_hex.d.ts +13 -2
  48. package/dist/esm/util/format_hex.d.ts.map +1 -1
  49. package/dist/esm/util/format_hex.js +6 -1
  50. package/dist/esm/util/format_hex.js.map +1 -1
  51. package/dist/web/js/{commission-node-dialog-DGw5qDgH.js → commission-node-dialog-B4_wgFye.js} +5 -5
  52. package/dist/web/js/{commission-node-existing-CHyyeC8y.js → commission-node-existing-BktL7vHX.js} +6 -5
  53. package/dist/web/js/{commission-node-thread-iRDSlidy.js → commission-node-thread-ox6fB3dO.js} +7 -6
  54. package/dist/web/js/{commission-node-wifi-C4YNR3bG.js → commission-node-wifi-Dlayi41Z.js} +9 -8
  55. package/dist/web/js/{dialog-box-ag-xOaYh.js → dialog-box-DFs0hcPv.js} +2 -2
  56. package/dist/web/js/{fire_event-BeiEbHcE.js → fire_event-oVtV3_P5.js} +2 -3
  57. package/dist/web/js/log-level-dialog-BVxKJJ49.js +3235 -0
  58. package/dist/web/js/main.js +170 -69
  59. package/dist/web/js/{matter-dashboard-app-BxQ4W_uT.js → matter-dashboard-app-Dr-IYMsD.js} +138 -17
  60. package/dist/web/js/{node-binding-dialog-ClziphM0.js → node-binding-dialog-CqE3VuZN.js} +6 -5
  61. package/dist/web/js/outlined-text-field-BVcHUxiS.js +968 -0
  62. package/dist/web/js/{prevent_default-Bs2sUnny.js → prevent_default-tLhqZmsK.js} +1 -1
  63. package/dist/web/js/validator-muF28wYx.js +1122 -0
  64. package/package.json +4 -4
  65. package/src/components/dialogs/binding/node-binding-dialog.ts +3 -2
  66. package/src/components/dialogs/commission-node-dialog/commission-node-existing.ts +2 -1
  67. package/src/components/dialogs/commission-node-dialog/commission-node-thread.ts +3 -2
  68. package/src/components/dialogs/commission-node-dialog/commission-node-wifi.ts +5 -4
  69. package/src/components/dialogs/settings/log-level-dialog.ts +183 -0
  70. package/src/components/dialogs/settings/show-log-level-dialog.ts +14 -0
  71. package/src/pages/cluster-commands/clusters/level-control-commands.ts +5 -2
  72. package/src/pages/cluster-commands/clusters/on-off-commands.ts +6 -3
  73. package/src/pages/components/header.ts +16 -1
  74. package/src/pages/components/node-details.ts +6 -5
  75. package/src/pages/components/server-details.ts +3 -3
  76. package/src/pages/matter-server-view.ts +17 -2
  77. package/src/util/async-handler.ts +74 -0
  78. package/src/util/fire_event.ts +1 -3
  79. package/src/util/format_hex.ts +17 -2
  80. package/dist/web/js/outlined-text-field-B-CiqgEJ.js +0 -2086
@@ -146,6 +146,8 @@ class Connection {
146
146
  };
147
147
  this.socket.onclose = () => {
148
148
  console.log("WebSocket Closed");
149
+ this.socket = void 0;
150
+ this.serverInfo = void 0;
149
151
  onConnectionLost();
150
152
  };
151
153
  this.socket.onerror = error => {
@@ -170,6 +172,7 @@ class Connection {
170
172
  this.socket.close();
171
173
  this.socket = void 0;
172
174
  }
175
+ this.serverInfo = void 0;
173
176
  }
174
177
  sendMessage(message) {
175
178
  if (!this.socket) {
@@ -187,6 +190,20 @@ class Connection {
187
190
  */
188
191
  class MatterError extends Error {}
189
192
  class InvalidServerVersion extends MatterError {}
193
+ class CommandTimeoutError extends MatterError {
194
+ constructor(command, timeoutMs) {
195
+ super(`Command '${command}' timed out after ${timeoutMs}ms`);
196
+ this.command = command;
197
+ this.timeoutMs = timeoutMs;
198
+ this.name = "CommandTimeoutError";
199
+ }
200
+ }
201
+ class ConnectionClosedError extends MatterError {
202
+ constructor(message = "Connection closed while command was pending") {
203
+ super(message);
204
+ this.name = "ConnectionClosedError";
205
+ }
206
+ }
190
207
 
191
208
  /**
192
209
  * @license
@@ -255,6 +272,7 @@ class MatterNode {
255
272
  function toNodeKey(nodeId) {
256
273
  return String(nodeId);
257
274
  }
275
+ const DEFAULT_COMMAND_TIMEOUT = 5 * 60 * 1e3;
258
276
  class MatterClient {
259
277
  /**
260
278
  * Create a new MatterClient.
@@ -274,9 +292,12 @@ class MatterClient {
274
292
  serverBaseAddress;
275
293
  /** Whether this client is connected to a production server (optional, for UI purposes) */
276
294
  isProduction = false;
295
+ /** Default timeout for commands in milliseconds. Set to 0 to disable timeouts. */
296
+ commandTimeout = DEFAULT_COMMAND_TIMEOUT;
277
297
  // Using 'unknown' for resolve since the actual types vary by command
278
298
  result_futures = {};
279
- msgId = 0;
299
+ // Start with random offset for defense-in-depth and easier debugging across sessions
300
+ msgId = Math.floor(Math.random() * 2147483647);
280
301
  eventListeners = {};
281
302
  get serverInfo() {
282
303
  return this.connection.serverInfo;
@@ -290,112 +311,112 @@ class MatterClient {
290
311
  this.eventListeners[event] = this.eventListeners[event].filter(l => l !== listener);
291
312
  };
292
313
  }
293
- async commissionWithCode(code, networkOnly = true) {
314
+ async commissionWithCode(code, networkOnly = true, timeout) {
294
315
  return await this.sendCommand("commission_with_code", 0, {
295
316
  code,
296
317
  network_only: networkOnly
297
- });
318
+ }, timeout);
298
319
  }
299
- async setWifiCredentials(ssid, credentials) {
320
+ async setWifiCredentials(ssid, credentials, timeout) {
300
321
  await this.sendCommand("set_wifi_credentials", 0, {
301
322
  ssid,
302
323
  credentials
303
- });
324
+ }, timeout);
304
325
  }
305
- async setThreadOperationalDataset(dataset) {
326
+ async setThreadOperationalDataset(dataset, timeout) {
306
327
  await this.sendCommand("set_thread_dataset", 0, {
307
328
  dataset
308
- });
329
+ }, timeout);
309
330
  }
310
- async openCommissioningWindow(nodeId, timeout, iteration, option, discriminator) {
331
+ async openCommissioningWindow(nodeId, windowTimeout, iteration, option, discriminator, timeout) {
311
332
  return await this.sendCommand("open_commissioning_window", 0, {
312
333
  node_id: nodeId,
313
- timeout,
334
+ timeout: windowTimeout,
314
335
  iteration,
315
336
  option,
316
337
  discriminator
317
- });
338
+ }, timeout);
318
339
  }
319
- async discoverCommissionableNodes() {
320
- return await this.sendCommand("discover_commissionable_nodes", 0, {});
340
+ async discoverCommissionableNodes(timeout) {
341
+ return await this.sendCommand("discover_commissionable_nodes", 0, {}, timeout);
321
342
  }
322
- async getMatterFabrics(nodeId) {
343
+ async getMatterFabrics(nodeId, timeout) {
323
344
  return await this.sendCommand("get_matter_fabrics", 3, {
324
345
  node_id: nodeId
325
- });
346
+ }, timeout);
326
347
  }
327
- async removeMatterFabric(nodeId, fabricIndex) {
348
+ async removeMatterFabric(nodeId, fabricIndex, timeout) {
328
349
  await this.sendCommand("remove_matter_fabric", 3, {
329
350
  node_id: nodeId,
330
351
  fabric_index: fabricIndex
331
- });
352
+ }, timeout);
332
353
  }
333
- async pingNode(nodeId, attempts = 1) {
354
+ async pingNode(nodeId, attempts = 1, timeout) {
334
355
  return await this.sendCommand("ping_node", 0, {
335
356
  node_id: nodeId,
336
357
  attempts
337
- });
358
+ }, timeout);
338
359
  }
339
- async getNodeIPAddresses(nodeId, preferCache, scoped) {
360
+ async getNodeIPAddresses(nodeId, preferCache, scoped, timeout) {
340
361
  return await this.sendCommand("get_node_ip_addresses", 8, {
341
362
  node_id: nodeId,
342
363
  prefer_cache: preferCache,
343
364
  scoped
344
- });
365
+ }, timeout);
345
366
  }
346
- async removeNode(nodeId) {
367
+ async removeNode(nodeId, timeout) {
347
368
  await this.sendCommand("remove_node", 0, {
348
369
  node_id: nodeId
349
- });
370
+ }, timeout);
350
371
  }
351
- async interviewNode(nodeId) {
372
+ async interviewNode(nodeId, timeout) {
352
373
  await this.sendCommand("interview_node", 0, {
353
374
  node_id: nodeId
354
- });
375
+ }, timeout);
355
376
  }
356
- async importTestNode(dump) {
377
+ async importTestNode(dump, timeout) {
357
378
  await this.sendCommand("import_test_node", 0, {
358
379
  dump
359
- });
380
+ }, timeout);
360
381
  }
361
- async readAttribute(nodeId, attributePath) {
382
+ async readAttribute(nodeId, attributePath, timeout) {
362
383
  return await this.sendCommand("read_attribute", 0, {
363
384
  node_id: nodeId,
364
385
  attribute_path: attributePath
365
- });
386
+ }, timeout);
366
387
  }
367
- async writeAttribute(nodeId, attributePath, value) {
388
+ async writeAttribute(nodeId, attributePath, value, timeout) {
368
389
  return await this.sendCommand("write_attribute", 0, {
369
390
  node_id: nodeId,
370
391
  attribute_path: attributePath,
371
392
  value
372
- });
393
+ }, timeout);
373
394
  }
374
- async checkNodeUpdate(nodeId) {
395
+ async checkNodeUpdate(nodeId, timeout) {
375
396
  return await this.sendCommand("check_node_update", 10, {
376
397
  node_id: nodeId
377
- });
398
+ }, timeout);
378
399
  }
379
- async updateNode(nodeId, softwareVersion) {
400
+ async updateNode(nodeId, softwareVersion, timeout) {
380
401
  await this.sendCommand("update_node", 10, {
381
402
  node_id: nodeId,
382
403
  software_version: softwareVersion
383
- });
404
+ }, timeout);
384
405
  }
385
- async setACLEntry(nodeId, entry) {
406
+ async setACLEntry(nodeId, entry, timeout) {
386
407
  return await this.sendCommand("set_acl_entry", 0, {
387
408
  node_id: nodeId,
388
409
  entry
389
- });
410
+ }, timeout);
390
411
  }
391
- async setNodeBinding(nodeId, endpoint, bindings) {
412
+ async setNodeBinding(nodeId, endpoint, bindings, timeout) {
392
413
  return await this.sendCommand("set_node_binding", 0, {
393
414
  node_id: nodeId,
394
415
  endpoint,
395
416
  bindings
396
- });
417
+ }, timeout);
397
418
  }
398
- async deviceCommand(nodeId, endpointId, clusterId, commandName, payload = {}) {
419
+ async deviceCommand(nodeId, endpointId, clusterId, commandName, payload = {}, timeout) {
399
420
  return await this.sendCommand("device_command", 0, {
400
421
  node_id: nodeId,
401
422
  endpoint_id: endpointId,
@@ -403,59 +424,147 @@ class MatterClient {
403
424
  command_name: commandName,
404
425
  payload,
405
426
  response_type: null
406
- });
427
+ }, timeout);
407
428
  }
408
- async getNodes(onlyAvailable = false) {
429
+ async getNodes(onlyAvailable = false, timeout) {
409
430
  return await this.sendCommand("get_nodes", 0, {
410
431
  only_available: onlyAvailable
411
- });
432
+ }, timeout);
412
433
  }
413
- async getNode(nodeId) {
434
+ async getNode(nodeId, timeout) {
414
435
  return await this.sendCommand("get_node", 0, {
415
436
  node_id: nodeId
416
- });
437
+ }, timeout);
417
438
  }
418
- async getVendorNames(filterVendors) {
439
+ async getVendorNames(filterVendors, timeout) {
419
440
  return await this.sendCommand("get_vendor_names", 0, {
420
441
  filter_vendors: filterVendors
421
- });
442
+ }, timeout);
422
443
  }
423
- async fetchServerInfo() {
424
- return await this.sendCommand("server_info", 0, {});
444
+ async fetchServerInfo(timeout) {
445
+ return await this.sendCommand("server_info", 0, {}, timeout);
425
446
  }
426
- async setDefaultFabricLabel(label) {
447
+ async setDefaultFabricLabel(label, timeout) {
427
448
  await this.sendCommand("set_default_fabric_label", 0, {
428
449
  label
429
- });
450
+ }, timeout);
451
+ }
452
+ /**
453
+ * Get the current log levels for console and file logging.
454
+ * @param timeout Optional command timeout in milliseconds
455
+ * @returns The current log level configuration
456
+ */
457
+ async getLogLevel(timeout) {
458
+ return await this.sendCommand("get_loglevel", 0, {}, timeout);
430
459
  }
431
- sendCommand(command, require_schema = void 0, args) {
460
+ /**
461
+ * Set the log level for console and/or file logging.
462
+ * Changes are temporary and will be reset when the server restarts.
463
+ * @param consoleLoglevel Console log level to set (optional)
464
+ * @param fileLoglevel File log level to set, only applied if file logging is enabled (optional)
465
+ * @param timeout Optional command timeout in milliseconds
466
+ * @returns The log level configuration after the change
467
+ */
468
+ async setLogLevel(consoleLoglevel, fileLoglevel, timeout) {
469
+ return await this.sendCommand("set_loglevel", 0, {
470
+ console_loglevel: consoleLoglevel,
471
+ file_loglevel: fileLoglevel
472
+ }, timeout);
473
+ }
474
+ /**
475
+ * Send a command to the Matter server.
476
+ * @param command The command name
477
+ * @param require_schema Minimum schema version required (0 for any version)
478
+ * @param args Command arguments
479
+ * @param timeout Optional timeout in milliseconds. Defaults to `commandTimeout`. Set to 0 to disable.
480
+ * @returns Promise that resolves with the command result
481
+ * @throws Error if the command times out or fails
482
+ */
483
+ sendCommand(command, require_schema = void 0, args, timeout = this.commandTimeout) {
432
484
  if (require_schema && this.serverInfo.schema_version < require_schema) {
433
485
  throw new InvalidServerVersion(`Command not available due to incompatible server version. Update the Matter Server to a version that supports at least api schema ${require_schema}.`);
434
486
  }
435
- const messageId = ++this.msgId;
487
+ if (this.msgId >= Number.MAX_SAFE_INTEGER) {
488
+ this.msgId = 0;
489
+ }
490
+ const messageId = String(++this.msgId);
436
491
  const message = {
437
- message_id: messageId.toString(),
492
+ message_id: messageId,
438
493
  command,
439
494
  args
440
495
  };
441
- const messagePromise = new Promise((resolve, reject) => {
496
+ return new Promise((resolve, reject) => {
497
+ let timeoutId;
498
+ if (timeout > 0) {
499
+ timeoutId = setTimeout(() => {
500
+ const pending = this.result_futures[messageId];
501
+ if (pending) {
502
+ if (pending.timeoutId) {
503
+ clearTimeout(pending.timeoutId);
504
+ }
505
+ delete this.result_futures[messageId];
506
+ reject(new CommandTimeoutError(command, timeout));
507
+ }
508
+ }, timeout);
509
+ }
442
510
  this.result_futures[messageId] = {
443
511
  resolve,
444
- reject
512
+ reject,
513
+ timeoutId
445
514
  };
446
515
  this.connection.sendMessage(message);
447
516
  });
448
- return messagePromise.finally(() => {
517
+ }
518
+ /**
519
+ * Safely resolve a pending command, ensuring it's only resolved once.
520
+ * Clears timeout and removes from pending futures before resolving.
521
+ */
522
+ _resolvePendingCommand(messageId, result) {
523
+ const pending = this.result_futures[messageId];
524
+ if (pending) {
525
+ if (pending.timeoutId) {
526
+ clearTimeout(pending.timeoutId);
527
+ }
449
528
  delete this.result_futures[messageId];
450
- });
529
+ pending.resolve(result);
530
+ }
531
+ }
532
+ /**
533
+ * Safely reject a pending command, ensuring it's only rejected once.
534
+ * Clears timeout and removes from pending futures before rejecting.
535
+ */
536
+ _rejectPendingCommand(messageId, error) {
537
+ const pending = this.result_futures[messageId];
538
+ if (pending) {
539
+ if (pending.timeoutId) {
540
+ clearTimeout(pending.timeoutId);
541
+ }
542
+ delete this.result_futures[messageId];
543
+ pending.reject(error);
544
+ }
545
+ }
546
+ /**
547
+ * Reject all pending commands with a ConnectionClosedError.
548
+ * Called when the connection is closed or lost.
549
+ */
550
+ _rejectAllPendingCommands() {
551
+ const error = new ConnectionClosedError();
552
+ const pendingIds = Object.keys(this.result_futures);
553
+ for (const messageId of pendingIds) {
554
+ this._rejectPendingCommand(messageId, error);
555
+ }
451
556
  }
452
557
  async connect() {
453
558
  if (this.connection.connected) {
454
559
  return;
455
560
  }
456
- await this.connection.connect(msg => this._handleIncomingMessage(msg), () => this.fireEvent("connection_lost"));
561
+ await this.connection.connect(msg => this._handleIncomingMessage(msg), () => {
562
+ this._rejectAllPendingCommands();
563
+ this.fireEvent("connection_lost");
564
+ });
457
565
  }
458
566
  disconnect(clearStorage = false) {
567
+ this._rejectAllPendingCommands();
459
568
  if (this.connection && this.connection.connected) {
460
569
  this.connection.disconnect();
461
570
  }
@@ -479,19 +588,11 @@ class MatterClient {
479
588
  return;
480
589
  }
481
590
  if ("error_code" in msg) {
482
- const promise = this.result_futures[msg.message_id];
483
- if (promise) {
484
- promise.reject(new Error(msg.details));
485
- delete this.result_futures[msg.message_id];
486
- }
591
+ this._rejectPendingCommand(msg.message_id, new Error(msg.details));
487
592
  return;
488
593
  }
489
594
  if ("result" in msg) {
490
- const promise = this.result_futures[msg.message_id];
491
- if (promise) {
492
- promise.resolve(msg.result);
493
- delete this.result_futures[msg.message_id];
494
- }
595
+ this._resolvePendingCommand(msg.message_id, msg.result);
495
596
  return;
496
597
  }
497
598
  console.warn("Received message with unknown format", msg);
@@ -641,7 +742,7 @@ const ThemeService = new ThemeServiceImpl();
641
742
  * SPDX-License-Identifier: Apache-2.0
642
743
  */
643
744
  async function main() {
644
- import('./matter-dashboard-app-BxQ4W_uT.js').then(function (n) { return n.s; });
745
+ import('./matter-dashboard-app-Dr-IYMsD.js').then(function (n) { return n.C; });
645
746
  let url = "";
646
747
  const isProductionServer = location.origin.includes(":5580") || location.href.includes("hassio_ingress") || location.href.includes("/api/ingress/");
647
748
  if (!isProductionServer) {