@matter-server/dashboard 0.7.2-alpha.0-20260601-e778038 → 0.8.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 (61) hide show
  1. package/dist/esm/components/dialogs/node-label-dialog/node-label-dialog.d.ts +28 -0
  2. package/dist/esm/components/dialogs/node-label-dialog/node-label-dialog.d.ts.map +1 -0
  3. package/dist/esm/components/dialogs/node-label-dialog/node-label-dialog.js +99 -0
  4. package/dist/esm/components/dialogs/node-label-dialog/node-label-dialog.js.map +6 -0
  5. package/dist/esm/components/dialogs/node-label-dialog/show-node-label-dialog.d.ts +8 -0
  6. package/dist/esm/components/dialogs/node-label-dialog/show-node-label-dialog.d.ts.map +1 -0
  7. package/dist/esm/components/dialogs/node-label-dialog/show-node-label-dialog.js +16 -0
  8. package/dist/esm/components/dialogs/node-label-dialog/show-node-label-dialog.js.map +6 -0
  9. package/dist/esm/components/dialogs/settings/log-level-section.d.ts.map +1 -1
  10. package/dist/esm/components/dialogs/settings/log-level-section.js +1 -0
  11. package/dist/esm/components/dialogs/settings/log-level-section.js.map +1 -1
  12. package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.d.ts.map +1 -1
  13. package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.js +7 -18
  14. package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.js.map +1 -1
  15. package/dist/esm/pages/components/header.d.ts +2 -0
  16. package/dist/esm/pages/components/header.d.ts.map +1 -1
  17. package/dist/esm/pages/components/header.js +13 -4
  18. package/dist/esm/pages/components/header.js.map +1 -1
  19. package/dist/esm/pages/components/node-details.d.ts +1 -0
  20. package/dist/esm/pages/components/node-details.d.ts.map +1 -1
  21. package/dist/esm/pages/components/node-details.js +28 -2
  22. package/dist/esm/pages/components/node-details.js.map +1 -1
  23. package/dist/esm/pages/matter-network-view.js +4 -4
  24. package/dist/esm/pages/matter-network-view.js.map +1 -1
  25. package/dist/esm/pages/network/thread-graph.d.ts +5 -6
  26. package/dist/esm/pages/network/thread-graph.d.ts.map +1 -1
  27. package/dist/esm/pages/network/thread-graph.js +39 -20
  28. package/dist/esm/pages/network/thread-graph.js.map +1 -1
  29. package/dist/esm/util/device-icons.d.ts +12 -3
  30. package/dist/esm/util/device-icons.d.ts.map +1 -1
  31. package/dist/esm/util/device-icons.js +28 -13
  32. package/dist/esm/util/device-icons.js.map +1 -1
  33. package/dist/esm/util/node-label.d.ts +16 -0
  34. package/dist/esm/util/node-label.d.ts.map +1 -0
  35. package/dist/esm/util/node-label.js +20 -0
  36. package/dist/esm/util/node-label.js.map +6 -0
  37. package/dist/web/index.html +6 -0
  38. package/dist/web/js/{attribute-write-dialog-BfQ9Xflh.js → attribute-write-dialog-f6XnfRjW.js} +1 -1
  39. package/dist/web/js/{command-invoke-dialog-Zj6gySV_.js → command-invoke-dialog-jzzpwI81.js} +1 -1
  40. package/dist/web/js/{commission-node-dialog-BpEVqGkZ.js → commission-node-dialog-B3RBDYQU.js} +5 -5
  41. package/dist/web/js/{commission-node-existing-4zO8iG_s.js → commission-node-existing-Cf6SG9fC.js} +2 -2
  42. package/dist/web/js/{commission-node-thread-AHWmXDx1.js → commission-node-thread-BlC2ZhGB.js} +2 -2
  43. package/dist/web/js/{commission-node-wifi-C07wuota.js → commission-node-wifi-BzeVHU3H.js} +2 -2
  44. package/dist/web/js/{dialog-box-BVHU0m4j.js → dialog-box-8CugytjX.js} +1 -1
  45. package/dist/web/js/{fire_event-YKA6y_5c.js → fire_event-B0dFAh3R.js} +1 -1
  46. package/dist/web/js/main.js +4 -4
  47. package/dist/web/js/{matter-dashboard-app-DIak2OyX.js → matter-dashboard-app-D7-YX94X.js} +170 -66
  48. package/dist/web/js/{node-binding-dialog-DILw-ecn.js → node-binding-dialog-Dpjn-GtG.js} +1 -1
  49. package/dist/web/js/node-label-dialog-B31BzWy6.js +80 -0
  50. package/dist/web/js/{settings-dialog-CBVhNIXT.js → settings-dialog-Di2Qnh3-.js} +4 -1
  51. package/package.json +7 -7
  52. package/src/components/dialogs/node-label-dialog/node-label-dialog.ts +93 -0
  53. package/src/components/dialogs/node-label-dialog/show-node-label-dialog.ts +15 -0
  54. package/src/components/dialogs/settings/log-level-section.ts +1 -0
  55. package/src/pages/cluster-commands/clusters/basic-information-commands.ts +7 -26
  56. package/src/pages/components/header.ts +15 -4
  57. package/src/pages/components/node-details.ts +31 -2
  58. package/src/pages/matter-network-view.ts +4 -4
  59. package/src/pages/network/thread-graph.ts +46 -22
  60. package/src/util/device-icons.ts +59 -14
  61. package/src/util/node-label.ts +22 -0
@@ -17,6 +17,7 @@ import {
17
17
  mdiCast,
18
18
  mdiCctv,
19
19
  mdiChip,
20
+ mdiCircleMedium,
20
21
  mdiCrown,
21
22
  mdiDishwasher,
22
23
  mdiDoorbell,
@@ -41,12 +42,15 @@ import {
41
42
  mdiRobotVacuum,
42
43
  mdiRouter,
43
44
  mdiRouterWireless,
45
+ mdiSleep,
44
46
  mdiSmokeDetector,
45
47
  mdiSnowflakeAlert,
46
48
  mdiSolarPower,
47
49
  mdiSpeaker,
48
50
  mdiSprinkler,
51
+ mdiStar,
49
52
  mdiStove,
53
+ mdiSwapHorizontal,
50
54
  mdiTelevision,
51
55
  mdiThermometer,
52
56
  mdiToggleSwitch,
@@ -304,6 +308,19 @@ const threadRoleToIcon: Record<number, string> = {
304
308
  6: mdiAccessPoint, // Leader
305
309
  };
306
310
 
311
+ /**
312
+ * Corner badge marking a node's Thread RoutingRole (attr 0/53/1) — a role-rank indicator overlaid on
313
+ * the device icon. The Leader is the rare, high-signal exception (amber crown); routers and end
314
+ * devices use progressively lower-key glyphs. Unassigned/Unspecified and unknown roles get no badge.
315
+ */
316
+ const THREAD_ROLE_BADGES: Record<number, { iconPath: string; colorVar: string; colorFallback: string }> = {
317
+ 2: { iconPath: mdiSleep, colorVar: "--node-color-thread-enddevice", colorFallback: "#90a4ae" }, // Sleepy End Device
318
+ 3: { iconPath: mdiCircleMedium, colorVar: "--node-color-thread-enddevice", colorFallback: "#90a4ae" }, // End Device
319
+ 4: { iconPath: mdiCircleMedium, colorVar: "--node-color-thread-enddevice", colorFallback: "#90a4ae" }, // REED
320
+ 5: { iconPath: mdiSwapHorizontal, colorVar: "--node-color-thread-router", colorFallback: "#1e88e5" }, // Router
321
+ 6: { iconPath: mdiCrown, colorVar: "--node-color-thread-leader", colorFallback: "#f9a825" }, // Leader
322
+ };
323
+
307
324
  /**
308
325
  * Utility device types (per Matter spec) deprioritized when selecting the primary type for icon display.
309
326
  * These are commonly reported alongside the actual application type (e.g., a light also reports as
@@ -454,14 +471,26 @@ export function getNetworkTypeIcon(networkType: string): string {
454
471
  * @param iconPath - The MDI icon path
455
472
  * @param color - The icon color (CSS color string)
456
473
  * @param size - The icon size in pixels
474
+ * @param badge - Optional top-right corner badge: an MDI glyph filled white on a colored disc
457
475
  * @returns A data URL containing the SVG
458
476
  */
459
- export function createIconDataUrl(iconPath: string, color: string, size: number = 48): string {
477
+ export function createIconDataUrl(
478
+ iconPath: string,
479
+ color: string,
480
+ size: number = 48,
481
+ badge?: { iconPath: string; color: string },
482
+ ): string {
460
483
  // MDI icons use a 24x24 viewBox
484
+ const badgeMarkup =
485
+ badge !== undefined
486
+ ? `<circle cx="18" cy="6" r="4" fill="${badge.color}"/>
487
+ <path d="${badge.iconPath}" fill="white" transform="translate(14.64,2.64) scale(0.28)"/>`
488
+ : "";
461
489
  const svg = `
462
490
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="${size}" height="${size}">
463
491
  <circle cx="12" cy="12" r="11" fill="white" stroke="${color}" stroke-width="1"/>
464
492
  <path d="${iconPath}" fill="${color}" transform="scale(0.6) translate(8,8)"/>
493
+ ${badgeMarkup}
465
494
  </svg>
466
495
  `.trim();
467
496
 
@@ -493,7 +522,14 @@ export function createNodeIconDataUrl(
493
522
  } else {
494
523
  color = getDefaultIconColor(); // Theme-aware default
495
524
  }
496
- return createIconDataUrl(iconPath, color);
525
+ // Thread RoutingRole (incl. Leader) applies to any router node, not just BRs. Badge it over the
526
+ // device icon rather than replacing the icon, preserving device identity.
527
+ const roleBadge = threadRole !== undefined ? THREAD_ROLE_BADGES[threadRole] : undefined;
528
+ const badge =
529
+ roleBadge !== undefined
530
+ ? { iconPath: roleBadge.iconPath, color: getCssVar(roleBadge.colorVar, roleBadge.colorFallback) }
531
+ : undefined;
532
+ return createIconDataUrl(iconPath, color, 48, badge);
497
533
  }
498
534
 
499
535
  /**
@@ -512,22 +548,31 @@ export function createUnknownDeviceIconDataUrl(isRouter: boolean = false, isSele
512
548
 
513
549
  /**
514
550
  * Creates an SVG data URL for a Thread Border Router identified via mDNS.
515
- * Leader BRs render with a crown glyph and amber color to differentiate from peers.
551
+ *
552
+ * Thread Leader (mesh routing role) and Primary BBR (backbone role) are orthogonal: the central
553
+ * glyph reflects the mesh role (crown for leader, router otherwise) while a corner star badge marks
554
+ * the primary BBR. A BR that is both shows both.
555
+ *
516
556
  * @param isSelected - Whether the node is selected
517
557
  * @param isLeader - Whether this BR is the Thread network leader (from MeshCoP state bitmap)
558
+ * @param isPrimaryBbr - Whether this BR is the primary Backbone Border Router (from MeshCoP state bitmap)
518
559
  * @returns A data URL containing the SVG
519
560
  */
520
- export function createBorderRouterIconDataUrl(isSelected: boolean = false, isLeader: boolean = false): string {
521
- if (isSelected) {
522
- return createIconDataUrl(
523
- isLeader ? mdiCrown : mdiRouterWireless,
524
- getCssVar("--node-color-selected", "#1976d2"),
525
- );
526
- }
527
- if (isLeader) {
528
- return createIconDataUrl(mdiCrown, getCssVar("--node-color-thread-leader", "#f9a825"));
529
- }
530
- return createIconDataUrl(mdiRouterWireless, getCssVar("--md-sys-color-primary", "#03a9f4"));
561
+ export function createBorderRouterIconDataUrl(
562
+ isSelected: boolean = false,
563
+ isLeader: boolean = false,
564
+ isPrimaryBbr: boolean = false,
565
+ ): string {
566
+ const glyph = isLeader ? mdiCrown : mdiRouterWireless;
567
+ const color = isSelected
568
+ ? getCssVar("--node-color-selected", "#1976d2")
569
+ : isLeader
570
+ ? getCssVar("--node-color-thread-leader", "#f9a825")
571
+ : getCssVar("--md-sys-color-primary", "#03a9f4");
572
+ const badge = isPrimaryBbr
573
+ ? { iconPath: mdiStar, color: getCssVar("--node-color-primary-bbr", "#00897b") }
574
+ : undefined;
575
+ return createIconDataUrl(glyph, color, 48, badge);
531
576
  }
532
577
 
533
578
  /**
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import type { MatterClient, MatterNode } from "@matter-server/ws-client";
8
+
9
+ // BasicInformation cluster (0x28 / 40), always on endpoint 0 per Matter specification.
10
+ export const NODE_LABEL_CLUSTER_ID = 0x28;
11
+ export const NODE_LABEL_ATTRIBUTE_ID = 5;
12
+ export const MAX_NODE_LABEL_LENGTH = 32;
13
+
14
+ export const NODE_LABEL_ATTRIBUTE_PATH = `0/${NODE_LABEL_CLUSTER_ID}/${NODE_LABEL_ATTRIBUTE_ID}`;
15
+
16
+ /**
17
+ * Write the BasicInformation NodeLabel attribute for a node.
18
+ * Trims surrounding whitespace so the stored value matches what MatterNode.nodeLabel reads back.
19
+ */
20
+ export function writeNodeLabel(client: MatterClient, node: MatterNode, label: string): Promise<unknown> {
21
+ return client.writeAttribute(node.node_id, NODE_LABEL_ATTRIBUTE_PATH, label.trim());
22
+ }