@matter-server/dashboard 0.3.2 → 0.3.3

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 (104) hide show
  1. package/dist/esm/pages/cluster-commands/base-cluster-commands.d.ts +2 -2
  2. package/dist/esm/pages/cluster-commands/base-cluster-commands.d.ts.map +1 -1
  3. package/dist/esm/pages/cluster-commands/base-cluster-commands.js.map +1 -1
  4. package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.d.ts +36 -0
  5. package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.d.ts.map +1 -0
  6. package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.js +159 -0
  7. package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.js.map +6 -0
  8. package/dist/esm/pages/cluster-commands/index.d.ts +1 -0
  9. package/dist/esm/pages/cluster-commands/index.d.ts.map +1 -1
  10. package/dist/esm/pages/cluster-commands/index.js +1 -0
  11. package/dist/esm/pages/cluster-commands/index.js.map +1 -1
  12. package/dist/esm/pages/components/footer.d.ts.map +1 -1
  13. package/dist/esm/pages/components/footer.js +4 -7
  14. package/dist/esm/pages/components/footer.js.map +1 -1
  15. package/dist/esm/pages/components/header.d.ts +5 -0
  16. package/dist/esm/pages/components/header.d.ts.map +1 -1
  17. package/dist/esm/pages/components/header.js +75 -0
  18. package/dist/esm/pages/components/header.js.map +1 -1
  19. package/dist/esm/pages/components/node-details.js +1 -1
  20. package/dist/esm/pages/components/node-details.js.map +1 -1
  21. package/dist/esm/pages/components/server-details.d.ts.map +1 -1
  22. package/dist/esm/pages/components/server-details.js +0 -1
  23. package/dist/esm/pages/components/server-details.js.map +1 -1
  24. package/dist/esm/pages/matter-dashboard-app.d.ts +12 -0
  25. package/dist/esm/pages/matter-dashboard-app.d.ts.map +1 -1
  26. package/dist/esm/pages/matter-dashboard-app.js +84 -4
  27. package/dist/esm/pages/matter-dashboard-app.js.map +1 -1
  28. package/dist/esm/pages/matter-network-view.d.ts +52 -0
  29. package/dist/esm/pages/matter-network-view.d.ts.map +1 -0
  30. package/dist/esm/pages/matter-network-view.js +309 -0
  31. package/dist/esm/pages/matter-network-view.js.map +6 -0
  32. package/dist/esm/pages/matter-node-view.d.ts.map +1 -1
  33. package/dist/esm/pages/matter-node-view.js +70 -1
  34. package/dist/esm/pages/matter-node-view.js.map +1 -1
  35. package/dist/esm/pages/matter-server-view.d.ts +4 -0
  36. package/dist/esm/pages/matter-server-view.d.ts.map +1 -1
  37. package/dist/esm/pages/matter-server-view.js +16 -1
  38. package/dist/esm/pages/matter-server-view.js.map +1 -1
  39. package/dist/esm/pages/network/base-network-graph.d.ts +74 -0
  40. package/dist/esm/pages/network/base-network-graph.d.ts.map +1 -0
  41. package/dist/esm/pages/network/base-network-graph.js +403 -0
  42. package/dist/esm/pages/network/base-network-graph.js.map +6 -0
  43. package/dist/esm/pages/network/device-icons.d.ts +52 -0
  44. package/dist/esm/pages/network/device-icons.d.ts.map +1 -0
  45. package/dist/esm/pages/network/device-icons.js +197 -0
  46. package/dist/esm/pages/network/device-icons.js.map +6 -0
  47. package/dist/esm/pages/network/device-panel.d.ts +31 -0
  48. package/dist/esm/pages/network/device-panel.d.ts.map +1 -0
  49. package/dist/esm/pages/network/device-panel.js +183 -0
  50. package/dist/esm/pages/network/device-panel.js.map +6 -0
  51. package/dist/esm/pages/network/network-details.d.ts +47 -0
  52. package/dist/esm/pages/network/network-details.d.ts.map +1 -0
  53. package/dist/esm/pages/network/network-details.js +686 -0
  54. package/dist/esm/pages/network/network-details.js.map +6 -0
  55. package/dist/esm/pages/network/network-types.d.ts +153 -0
  56. package/dist/esm/pages/network/network-types.d.ts.map +1 -0
  57. package/dist/esm/pages/network/network-types.js +19 -0
  58. package/dist/esm/pages/network/network-types.js.map +6 -0
  59. package/dist/esm/pages/network/network-utils.d.ts +170 -0
  60. package/dist/esm/pages/network/network-utils.d.ts.map +1 -0
  61. package/dist/esm/pages/network/network-utils.js +472 -0
  62. package/dist/esm/pages/network/network-utils.js.map +6 -0
  63. package/dist/esm/pages/network/thread-graph.d.ts +27 -0
  64. package/dist/esm/pages/network/thread-graph.d.ts.map +1 -0
  65. package/dist/esm/pages/network/thread-graph.js +134 -0
  66. package/dist/esm/pages/network/thread-graph.js.map +6 -0
  67. package/dist/esm/pages/network/wifi-graph.d.ts +27 -0
  68. package/dist/esm/pages/network/wifi-graph.d.ts.map +1 -0
  69. package/dist/esm/pages/network/wifi-graph.js +167 -0
  70. package/dist/esm/pages/network/wifi-graph.js.map +6 -0
  71. package/dist/web/js/{commission-node-dialog-CBSDiqRW.js → commission-node-dialog-B1_khzZb.js} +5 -5
  72. package/dist/web/js/{commission-node-existing-TP6s8Tez.js → commission-node-existing-RpdajrwF.js} +2 -5
  73. package/dist/web/js/{commission-node-thread-DOB8pu6x.js → commission-node-thread-5f2itkTG.js} +2 -5
  74. package/dist/web/js/{commission-node-wifi-tzavmk1j.js → commission-node-wifi-DZ_pWqsa.js} +2 -5
  75. package/dist/web/js/{dialog-box-Dknil_Be.js → dialog-box-DEUxM4B1.js} +2 -2
  76. package/dist/web/js/{fire_event-DRpOSjJR.js → fire_event-BczBMT8E.js} +1 -1
  77. package/dist/web/js/{log-level-dialog-TXkma-7Z.js → log-level-dialog-Cr3PfX1X.js} +2 -3
  78. package/dist/web/js/main.js +1 -1
  79. package/dist/web/js/matter-dashboard-app-BuCe_Jxf.js +29990 -0
  80. package/dist/web/js/{node-binding-dialog-D52FCBFP.js → node-binding-dialog-DMiHNDLA.js} +2 -4
  81. package/dist/web/js/{prevent_default-BPgSQsuY.js → prevent_default-D4FX_PIh.js} +2 -42
  82. package/package.json +5 -4
  83. package/src/pages/cluster-commands/base-cluster-commands.ts +2 -2
  84. package/src/pages/cluster-commands/clusters/basic-information-commands.ts +171 -0
  85. package/src/pages/cluster-commands/index.ts +1 -0
  86. package/src/pages/components/footer.ts +4 -7
  87. package/src/pages/components/header.ts +81 -0
  88. package/src/pages/components/node-details.ts +2 -2
  89. package/src/pages/components/server-details.ts +0 -1
  90. package/src/pages/matter-dashboard-app.ts +105 -5
  91. package/src/pages/matter-network-view.ts +325 -0
  92. package/src/pages/matter-node-view.ts +75 -1
  93. package/src/pages/matter-server-view.ts +17 -1
  94. package/src/pages/network/base-network-graph.ts +463 -0
  95. package/src/pages/network/device-icons.ts +283 -0
  96. package/src/pages/network/device-panel.ts +180 -0
  97. package/src/pages/network/network-details.ts +750 -0
  98. package/src/pages/network/network-types.ts +161 -0
  99. package/src/pages/network/network-utils.ts +752 -0
  100. package/src/pages/network/thread-graph.ts +164 -0
  101. package/src/pages/network/wifi-graph.ts +192 -0
  102. package/dist/web/js/matter-dashboard-app-B7GUghkC.js +0 -17254
  103. package/dist/web/js/outlined-text-field-D1DyKQY-.js +0 -968
  104. package/dist/web/js/validator-C735j770.js +0 -1122
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { html } from "lit";
8
+ import { customElement } from "lit/decorators.js";
9
+ import { BaseNetworkGraph } from "./base-network-graph.js";
10
+ import { createNodeIconDataUrl, createUnknownDeviceIconDataUrl } from "./device-icons.js";
11
+ import type { NetworkGraphEdge, NetworkGraphNode, UnknownThreadDevice } from "./network-types.js";
12
+ import {
13
+ buildExtAddrMap,
14
+ buildThreadConnections,
15
+ findUnknownDevices,
16
+ getDeviceName,
17
+ getNetworkType,
18
+ getThreadRole,
19
+ } from "./network-utils.js";
20
+
21
+ declare global {
22
+ interface HTMLElementTagNameMap {
23
+ "thread-graph": ThreadGraph;
24
+ }
25
+ }
26
+
27
+ @customElement("thread-graph")
28
+ export class ThreadGraph extends BaseNetworkGraph {
29
+ /** Cache of unknown devices for the current render */
30
+ private _unknownDevices: UnknownThreadDevice[] = [];
31
+
32
+ /** Cached map of unknown devices (rebuilt in _updateGraph) */
33
+ private _unknownDevicesMapCache: Map<
34
+ string,
35
+ { extAddressHex: string; isRouter: boolean; seenBy: string[]; bestRssi: number | null }
36
+ > = new Map();
37
+
38
+ /** Get unknown devices as a map for use by details panel */
39
+ public get unknownDevicesMap(): Map<
40
+ string,
41
+ { extAddressHex: string; isRouter: boolean; seenBy: string[]; bestRssi: number | null }
42
+ > {
43
+ return this._unknownDevicesMapCache;
44
+ }
45
+
46
+ protected override _updateGraph(): void {
47
+ if (!this._nodesDataSet || !this._edgesDataSet) return;
48
+
49
+ // Clear stored edge colors since we're rebuilding edges
50
+ this._clearOriginalEdgeColors();
51
+
52
+ // Filter to Thread devices only
53
+ const threadNodes = Object.values(this.nodes).filter(node => getNetworkType(node) === "thread");
54
+
55
+ if (threadNodes.length === 0) {
56
+ this._nodesDataSet.clear();
57
+ this._edgesDataSet.clear();
58
+ this._unknownDevices = [];
59
+ this._unknownDevicesMapCache.clear();
60
+ return;
61
+ }
62
+
63
+ // Build extended address map for connection matching
64
+ const extAddrMap = buildExtAddrMap(this.nodes);
65
+
66
+ // Find unknown devices (seen in neighbor tables but not commissioned)
67
+ this._unknownDevices = findUnknownDevices(this.nodes, extAddrMap);
68
+
69
+ // Rebuild the cached map
70
+ this._unknownDevicesMapCache.clear();
71
+ for (const device of this._unknownDevices) {
72
+ this._unknownDevicesMapCache.set(device.id, {
73
+ extAddressHex: device.extAddressHex,
74
+ isRouter: device.isRouter,
75
+ seenBy: device.seenBy,
76
+ bestRssi: device.bestRssi,
77
+ });
78
+ }
79
+
80
+ // Build Thread connections (including to unknown devices)
81
+ const connections = buildThreadConnections(this.nodes, extAddrMap, this._unknownDevices);
82
+
83
+ // Create node data for vis.js - known Thread devices
84
+ // Use string IDs to avoid precision loss for large bigint node IDs
85
+ const graphNodes: NetworkGraphNode[] = threadNodes.map(node => {
86
+ const nodeId = String(node.node_id);
87
+ const threadRole = getThreadRole(node);
88
+ const isSelected = nodeId === String(this._selectedNodeId);
89
+ const isOffline = node.available === false;
90
+
91
+ return {
92
+ id: nodeId,
93
+ label: getDeviceName(node),
94
+ image: createNodeIconDataUrl(node, threadRole, isSelected, isOffline),
95
+ shape: "image",
96
+ networkType: "thread",
97
+ threadRole,
98
+ offline: isOffline,
99
+ };
100
+ });
101
+
102
+ // Add unknown devices with question mark icons
103
+ for (const unknown of this._unknownDevices) {
104
+ const isSelected = unknown.id === this._selectedNodeId;
105
+ graphNodes.push({
106
+ id: unknown.id,
107
+ label: `Unknown (${unknown.extAddressHex.slice(-8)})`,
108
+ image: createUnknownDeviceIconDataUrl(unknown.isRouter, isSelected),
109
+ shape: "image",
110
+ networkType: "thread",
111
+ isUnknown: true,
112
+ });
113
+ }
114
+
115
+ // Create edge data for vis.js
116
+ const graphEdges: NetworkGraphEdge[] = connections.map((conn, index) => {
117
+ const isToUnknown = typeof conn.toNodeId === "string" && conn.toNodeId.startsWith("unknown_");
118
+ return {
119
+ id: `edge_${index}`,
120
+ from: conn.fromNodeId,
121
+ to: conn.toNodeId,
122
+ color: {
123
+ color: conn.signalColor,
124
+ highlight: conn.signalColor,
125
+ },
126
+ width: 2,
127
+ title: conn.rssi !== null ? `RSSI: ${conn.rssi} dBm, LQI: ${conn.lqi}` : `LQI: ${conn.lqi}`,
128
+ dashes: isToUnknown, // Dashed lines to unknown devices
129
+ };
130
+ });
131
+
132
+ // Update datasets
133
+ const existingNodeIds = this._nodesDataSet.getIds();
134
+ const newNodeIds = new Set(graphNodes.map(n => n.id));
135
+
136
+ // Remove nodes that no longer exist
137
+ const nodesToRemove = existingNodeIds.filter((id: string | number) => !newNodeIds.has(id));
138
+ if (nodesToRemove.length > 0) {
139
+ this._nodesDataSet.remove(nodesToRemove);
140
+ }
141
+
142
+ // Update or add nodes
143
+ this._nodesDataSet.update(graphNodes);
144
+
145
+ // Replace all edges (simpler than diff for edge data)
146
+ this._edgesDataSet.clear();
147
+ this._edgesDataSet.add(graphEdges);
148
+ }
149
+
150
+ override render() {
151
+ const threadNodes = Object.values(this.nodes).filter(node => getNetworkType(node) === "thread");
152
+
153
+ if (threadNodes.length === 0) {
154
+ return html`
155
+ <div class="empty-state">
156
+ <p>No Thread devices found</p>
157
+ <p class="hint">Thread devices will appear here once commissioned</p>
158
+ </div>
159
+ `;
160
+ }
161
+
162
+ return html`<div class="graph-container"></div>`;
163
+ }
164
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { html } from "lit";
8
+ import { customElement } from "lit/decorators.js";
9
+ import { BaseNetworkGraph } from "./base-network-graph.js";
10
+ import { createNodeIconDataUrl, createWiFiRouterIconDataUrl } from "./device-icons.js";
11
+ import type { NetworkGraphEdge, NetworkGraphNode } from "./network-types.js";
12
+ import {
13
+ categorizeDevices,
14
+ getDeviceName,
15
+ getNetworkType,
16
+ getSignalColorFromRssi,
17
+ getWiFiDiagnostics,
18
+ } from "./network-utils.js";
19
+
20
+ declare global {
21
+ interface HTMLElementTagNameMap {
22
+ "wifi-graph": WiFiGraph;
23
+ }
24
+ }
25
+
26
+ /** WiFi access point (router) node info */
27
+ interface WiFiAccessPoint {
28
+ bssid: string;
29
+ /** Connected node IDs as strings to avoid BigInt precision loss */
30
+ connectedNodes: string[];
31
+ }
32
+
33
+ @customElement("wifi-graph")
34
+ export class WiFiGraph extends BaseNetworkGraph {
35
+ /** Cache of access points for the current render */
36
+ private _accessPoints: Map<string, WiFiAccessPoint> = new Map();
37
+
38
+ /** Get access points map for use by details panel */
39
+ public get wifiAccessPointsMap(): Map<string, { bssid: string; connectedNodes: string[] }> {
40
+ return this._accessPoints;
41
+ }
42
+
43
+ /**
44
+ * Override physics for WiFi star topology - needs stronger cluster separation.
45
+ */
46
+ protected override _getPhysicsOptions(): any {
47
+ return {
48
+ enabled: true,
49
+ solver: "forceAtlas2Based",
50
+ forceAtlas2Based: {
51
+ gravitationalConstant: -120, // Stronger repulsion for star topology
52
+ centralGravity: 0.003, // Weaker central pull
53
+ springLength: 100, // Shorter springs keep devices close to their AP
54
+ springConstant: 0.12, // Stronger springs
55
+ damping: 0.4,
56
+ avoidOverlap: 0.8,
57
+ },
58
+ stabilization: {
59
+ enabled: true,
60
+ iterations: 300,
61
+ updateInterval: 25,
62
+ },
63
+ };
64
+ }
65
+
66
+ protected override _updateGraph(): void {
67
+ if (!this._nodesDataSet || !this._edgesDataSet) return;
68
+
69
+ // Clear stored edge colors since we're rebuilding edges
70
+ this._clearOriginalEdgeColors();
71
+
72
+ // Get WiFi and Ethernet devices
73
+ const categorized = categorizeDevices(this.nodes);
74
+ const wifiNodeIds = [...categorized.wifi, ...categorized.ethernet];
75
+
76
+ if (wifiNodeIds.length === 0) {
77
+ this._nodesDataSet.clear();
78
+ this._edgesDataSet.clear();
79
+ this._accessPoints.clear();
80
+ return;
81
+ }
82
+
83
+ // Build access points map from BSSID, keyed by apId
84
+ // wifiNodeIds are already strings from categorizeDevices
85
+ this._accessPoints.clear();
86
+
87
+ for (const nodeId of wifiNodeIds) {
88
+ const node = this.nodes[nodeId];
89
+ if (!node) continue;
90
+
91
+ const wifiDiag = getWiFiDiagnostics(node);
92
+ if (wifiDiag.bssid) {
93
+ const apId = `ap_${wifiDiag.bssid.replace(/:/g, "")}`;
94
+ if (!this._accessPoints.has(apId)) {
95
+ this._accessPoints.set(apId, {
96
+ bssid: wifiDiag.bssid,
97
+ connectedNodes: [],
98
+ });
99
+ }
100
+ this._accessPoints.get(apId)!.connectedNodes.push(nodeId);
101
+ }
102
+ }
103
+
104
+ // Create graph nodes
105
+ const graphNodes: NetworkGraphNode[] = [];
106
+ const graphEdges: NetworkGraphEdge[] = [];
107
+
108
+ // Add access point nodes
109
+ for (const [apId, ap] of this._accessPoints) {
110
+ const bssid = ap.bssid;
111
+ const isSelected = String(apId) === String(this._selectedNodeId);
112
+
113
+ graphNodes.push({
114
+ id: apId,
115
+ label: `AP ${bssid.slice(-8)}`,
116
+ image: createWiFiRouterIconDataUrl(isSelected),
117
+ shape: "image",
118
+ networkType: "wifi",
119
+ isUnknown: true, // Mark as infrastructure
120
+ });
121
+ }
122
+
123
+ // Add device nodes and edges
124
+ // nodeId is already a string from categorizeDevices
125
+ let edgeIndex = 0;
126
+ for (const nodeId of wifiNodeIds) {
127
+ const node = this.nodes[nodeId];
128
+ if (!node) continue;
129
+
130
+ const isSelected = nodeId === String(this._selectedNodeId);
131
+ const isOffline = node.available === false;
132
+ const networkType = getNetworkType(node);
133
+ const wifiDiag = getWiFiDiagnostics(node);
134
+
135
+ graphNodes.push({
136
+ id: nodeId,
137
+ label: getDeviceName(node),
138
+ image: createNodeIconDataUrl(node, undefined, isSelected, isOffline),
139
+ shape: "image",
140
+ networkType: networkType,
141
+ offline: isOffline,
142
+ });
143
+
144
+ // Create edge to access point if we have BSSID
145
+ if (wifiDiag.bssid) {
146
+ const apId = `ap_${wifiDiag.bssid.replace(/:/g, "")}`;
147
+ const signalColor = getSignalColorFromRssi(wifiDiag.rssi);
148
+
149
+ graphEdges.push({
150
+ id: `edge_${edgeIndex++}`,
151
+ from: nodeId,
152
+ to: apId,
153
+ color: {
154
+ color: signalColor,
155
+ highlight: signalColor,
156
+ },
157
+ width: 2,
158
+ title: wifiDiag.rssi !== null ? `RSSI: ${wifiDiag.rssi} dBm` : "RSSI: Unknown",
159
+ });
160
+ }
161
+ }
162
+
163
+ // Update datasets
164
+ const existingNodeIds = this._nodesDataSet.getIds();
165
+ const newNodeIds = new Set(graphNodes.map(n => n.id));
166
+
167
+ const nodesToRemove = existingNodeIds.filter((id: string | number) => !newNodeIds.has(id));
168
+ if (nodesToRemove.length > 0) {
169
+ this._nodesDataSet.remove(nodesToRemove);
170
+ }
171
+
172
+ this._nodesDataSet.update(graphNodes);
173
+ this._edgesDataSet.clear();
174
+ this._edgesDataSet.add(graphEdges);
175
+ }
176
+
177
+ override render() {
178
+ const categorized = categorizeDevices(this.nodes);
179
+ const wifiCount = categorized.wifi.length + categorized.ethernet.length;
180
+
181
+ if (wifiCount === 0) {
182
+ return html`
183
+ <div class="empty-state">
184
+ <p>No WiFi devices found</p>
185
+ <p class="hint">WiFi devices will appear here once commissioned</p>
186
+ </div>
187
+ `;
188
+ }
189
+
190
+ return html`<div class="graph-container"></div>`;
191
+ }
192
+ }