@matter-server/dashboard 0.3.3 → 0.3.5-alpha.0-20260203-66f5d74
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/README.md +76 -0
- package/dist/esm/components/dialogs/binding/node-binding-dialog.d.ts.map +1 -1
- package/dist/esm/components/dialogs/binding/node-binding-dialog.js +44 -17
- package/dist/esm/components/dialogs/binding/node-binding-dialog.js.map +1 -1
- package/dist/esm/entrypoint/main.js +1 -1
- package/dist/esm/entrypoint/main.js.map +1 -1
- package/dist/esm/pages/components/node-details.js +1 -1
- package/dist/esm/pages/components/node-details.js.map +1 -1
- package/dist/esm/pages/matter-cluster-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-cluster-view.js +9 -4
- package/dist/esm/pages/matter-cluster-view.js.map +1 -1
- package/dist/esm/pages/matter-endpoint-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-endpoint-view.js +8 -2
- package/dist/esm/pages/matter-endpoint-view.js.map +1 -1
- package/dist/esm/pages/matter-network-view.d.ts +5 -0
- package/dist/esm/pages/matter-network-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-network-view.js +96 -13
- package/dist/esm/pages/matter-network-view.js.map +1 -1
- package/dist/esm/pages/matter-node-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-node-view.js +17 -3
- package/dist/esm/pages/matter-node-view.js.map +1 -1
- package/dist/esm/pages/network/base-network-graph.d.ts +34 -0
- package/dist/esm/pages/network/base-network-graph.d.ts.map +1 -1
- package/dist/esm/pages/network/base-network-graph.js +116 -25
- package/dist/esm/pages/network/base-network-graph.js.map +1 -1
- package/dist/esm/pages/network/device-icons.js +1 -1
- package/dist/esm/pages/network/network-details.d.ts +31 -1
- package/dist/esm/pages/network/network-details.d.ts.map +1 -1
- package/dist/esm/pages/network/network-details.js +233 -15
- package/dist/esm/pages/network/network-details.js.map +1 -1
- package/dist/esm/pages/network/network-types.d.ts +6 -0
- package/dist/esm/pages/network/network-types.d.ts.map +1 -1
- package/dist/esm/pages/network/network-utils.d.ts +26 -0
- package/dist/esm/pages/network/network-utils.d.ts.map +1 -1
- package/dist/esm/pages/network/network-utils.js +71 -3
- package/dist/esm/pages/network/network-utils.js.map +1 -1
- package/dist/esm/pages/network/thread-graph.d.ts.map +1 -1
- package/dist/esm/pages/network/thread-graph.js +5 -2
- package/dist/esm/pages/network/thread-graph.js.map +1 -1
- package/dist/esm/pages/network/update-connections-dialog.d.ts +55 -0
- package/dist/esm/pages/network/update-connections-dialog.d.ts.map +1 -0
- package/dist/esm/pages/network/update-connections-dialog.js +284 -0
- package/dist/esm/pages/network/update-connections-dialog.js.map +6 -0
- package/dist/esm/pages/network/wifi-graph.d.ts.map +1 -1
- package/dist/esm/pages/network/wifi-graph.js +5 -3
- package/dist/esm/pages/network/wifi-graph.js.map +1 -1
- package/dist/esm/util/format_hex.d.ts +18 -0
- package/dist/esm/util/format_hex.d.ts.map +1 -1
- package/dist/esm/util/format_hex.js +21 -1
- package/dist/esm/util/format_hex.js.map +1 -1
- package/dist/esm/util/matter-status.d.ts +49 -0
- package/dist/esm/util/matter-status.d.ts.map +1 -0
- package/dist/esm/util/matter-status.js +110 -0
- package/dist/esm/util/matter-status.js.map +6 -0
- package/dist/web/js/{commission-node-dialog-B1_khzZb.js → commission-node-dialog-DBugVQOl.js} +5 -5
- package/dist/web/js/{commission-node-existing-RpdajrwF.js → commission-node-existing-ts2MN2bQ.js} +2 -2
- package/dist/web/js/{commission-node-thread-5f2itkTG.js → commission-node-thread-CixfNuM3.js} +2 -2
- package/dist/web/js/{commission-node-wifi-DZ_pWqsa.js → commission-node-wifi-CWC30EYn.js} +2 -2
- package/dist/web/js/{dialog-box-DEUxM4B1.js → dialog-box-SeMuFiWx.js} +2 -2
- package/dist/web/js/{fire_event-BczBMT8E.js → fire_event-B63oGTcK.js} +1 -1
- package/dist/web/js/{log-level-dialog-Cr3PfX1X.js → log-level-dialog-J0gFiLLM.js} +2 -2
- package/dist/web/js/main.js +2 -2
- package/dist/web/js/{matter-dashboard-app-BuCe_Jxf.js → matter-dashboard-app-D7YWrgXj.js} +2041 -246
- package/dist/web/js/{node-binding-dialog-DMiHNDLA.js → node-binding-dialog-DSBwIJcQ.js} +147 -19
- package/dist/web/js/prevent_default-D-ohDGsN.js +8 -0
- package/package.json +4 -4
- package/src/components/dialogs/binding/node-binding-dialog.ts +45 -21
- package/src/entrypoint/main.ts +2 -0
- package/src/pages/components/node-details.ts +1 -1
- package/src/pages/matter-cluster-view.ts +11 -4
- package/src/pages/matter-endpoint-view.ts +10 -3
- package/src/pages/matter-network-view.ts +102 -13
- package/src/pages/matter-node-view.ts +19 -4
- package/src/pages/network/base-network-graph.ts +147 -33
- package/src/pages/network/device-icons.ts +1 -1
- package/src/pages/network/network-details.ts +281 -16
- package/src/pages/network/network-types.ts +6 -0
- package/src/pages/network/network-utils.ts +109 -0
- package/src/pages/network/thread-graph.ts +7 -1
- package/src/pages/network/update-connections-dialog.ts +327 -0
- package/src/pages/network/wifi-graph.ts +4 -3
- package/src/util/format_hex.ts +39 -0
- package/src/util/matter-status.ts +165 -0
- package/dist/web/js/prevent_default-D4FX_PIh.js +0 -774
|
@@ -8,12 +8,13 @@ import "@material/web/divider/divider";
|
|
|
8
8
|
import "@material/web/iconbutton/icon-button";
|
|
9
9
|
import "@material/web/list/list";
|
|
10
10
|
import "@material/web/list/list-item";
|
|
11
|
-
import { MatterClient, MatterNode } from "@matter-server/ws-client";
|
|
11
|
+
import { isTestNodeId, MatterClient, MatterNode } from "@matter-server/ws-client";
|
|
12
12
|
import { mdiChevronRight, mdiGraphOutline } from "@mdi/js";
|
|
13
|
-
import {
|
|
13
|
+
import { css, html, LitElement } from "lit";
|
|
14
14
|
import { customElement, property } from "lit/decorators.js";
|
|
15
15
|
import { guard } from "lit/directives/guard.js";
|
|
16
16
|
import "../components/ha-svg-icon";
|
|
17
|
+
import { formatNodeAddress, getEffectiveFabricIndex } from "../util/format_hex.js";
|
|
17
18
|
import "./components/header";
|
|
18
19
|
import "./components/node-details";
|
|
19
20
|
import { getEndpointDeviceTypes } from "./matter-endpoint-view.js";
|
|
@@ -54,9 +55,16 @@ class MatterNodeView extends LitElement {
|
|
|
54
55
|
const graphViewType = networkType === "ethernet" ? "wifi" : networkType;
|
|
55
56
|
const graphUrl = showGraphButton ? `#${graphViewType}/${this.node.node_id}` : null;
|
|
56
57
|
|
|
58
|
+
// Format node address for hex display
|
|
59
|
+
const fabricIndex = getEffectiveFabricIndex(
|
|
60
|
+
this.client.serverInfo.fabric_index,
|
|
61
|
+
isTestNodeId(this.node.node_id),
|
|
62
|
+
);
|
|
63
|
+
const nodeHex = formatNodeAddress(fabricIndex, this.node.node_id);
|
|
64
|
+
|
|
57
65
|
return html`
|
|
58
66
|
<dashboard-header
|
|
59
|
-
.title=${
|
|
67
|
+
.title=${`Node ${this.node.node_id} ${nodeHex}`}
|
|
60
68
|
.client=${this.client}
|
|
61
69
|
backButton="#"
|
|
62
70
|
></dashboard-header>
|
|
@@ -64,7 +72,7 @@ class MatterNodeView extends LitElement {
|
|
|
64
72
|
<!-- node details section -->
|
|
65
73
|
<div class="container">
|
|
66
74
|
<div class="node-title-bar">
|
|
67
|
-
<h2>Node ${this.node.node_id}</h2>
|
|
75
|
+
<h2>Node ${this.node.node_id} <span class="node-id-hex">${nodeHex}</span></h2>
|
|
68
76
|
${showGraphButton
|
|
69
77
|
? html`
|
|
70
78
|
<a href=${graphUrl} class="show-in-graph-button" title="Show in ${graphViewType} graph">
|
|
@@ -156,6 +164,13 @@ class MatterNodeView extends LitElement {
|
|
|
156
164
|
color: var(--md-sys-color-on-background, #333);
|
|
157
165
|
}
|
|
158
166
|
|
|
167
|
+
.node-id-hex {
|
|
168
|
+
font-size: 0.75em;
|
|
169
|
+
font-weight: 400;
|
|
170
|
+
color: var(--md-sys-color-on-surface-variant, #666);
|
|
171
|
+
font-family: monospace;
|
|
172
|
+
}
|
|
173
|
+
|
|
159
174
|
.show-in-graph-button {
|
|
160
175
|
display: inline-flex;
|
|
161
176
|
align-items: center;
|
|
@@ -23,6 +23,9 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
23
23
|
@state()
|
|
24
24
|
protected _selectedNodeId: number | string | null = null;
|
|
25
25
|
|
|
26
|
+
@state()
|
|
27
|
+
protected _physicsEnabled = true;
|
|
28
|
+
|
|
26
29
|
protected _network?: Network;
|
|
27
30
|
protected _nodesDataSet?: DataSet<NetworkGraphNode>;
|
|
28
31
|
protected _edgesDataSet?: DataSet<NetworkGraphEdge>;
|
|
@@ -30,6 +33,11 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
30
33
|
protected _resizeObserver?: ResizeObserver;
|
|
31
34
|
protected _themeUnsubscribe?: () => void;
|
|
32
35
|
protected _updateDebounceTimer?: ReturnType<typeof setTimeout>;
|
|
36
|
+
protected _autoFreezeTimer?: ReturnType<typeof setTimeout>;
|
|
37
|
+
/** Whether auto-freeze has already been applied (to avoid re-freezing after user unfreezes) */
|
|
38
|
+
private _autoFreezeApplied = false;
|
|
39
|
+
/** Whether initial fit has been done (to preserve user's zoom/pan after first load) */
|
|
40
|
+
private _initialFitDone = false;
|
|
33
41
|
|
|
34
42
|
/** Store original edge colors for restoration after highlighting */
|
|
35
43
|
private _originalEdgeColors: Map<string, { color: string; highlight: string }> = new Map();
|
|
@@ -133,17 +141,31 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
133
141
|
|
|
134
142
|
// Subscribe to theme changes - refresh entire graph to update colors
|
|
135
143
|
this._themeUnsubscribe = ThemeService.subscribe(() => {
|
|
136
|
-
if (this._network) {
|
|
137
|
-
|
|
144
|
+
if (this._network && this._nodesDataSet) {
|
|
145
|
+
const fontColor = this._getFontColor();
|
|
146
|
+
|
|
147
|
+
// Update default font colors for new nodes
|
|
138
148
|
this._network.setOptions({
|
|
139
149
|
nodes: {
|
|
140
150
|
font: {
|
|
141
|
-
color:
|
|
151
|
+
color: fontColor,
|
|
142
152
|
},
|
|
143
153
|
},
|
|
144
154
|
});
|
|
155
|
+
|
|
145
156
|
// Regenerate node icons and edges with new theme colors
|
|
146
157
|
this._updateGraph();
|
|
158
|
+
|
|
159
|
+
// Update font color on all existing nodes in the dataset
|
|
160
|
+
const allNodes = this._nodesDataSet.get();
|
|
161
|
+
const nodeUpdates = allNodes.map((node: NetworkGraphNode) => ({
|
|
162
|
+
id: node.id,
|
|
163
|
+
font: { color: fontColor },
|
|
164
|
+
}));
|
|
165
|
+
this._nodesDataSet.update(nodeUpdates);
|
|
166
|
+
|
|
167
|
+
// Force redraw
|
|
168
|
+
this._network.redraw();
|
|
147
169
|
}
|
|
148
170
|
});
|
|
149
171
|
}
|
|
@@ -152,6 +174,7 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
152
174
|
super.disconnectedCallback();
|
|
153
175
|
this._resizeObserver?.disconnect();
|
|
154
176
|
this._themeUnsubscribe?.();
|
|
177
|
+
this._cancelAutoFreezeTimer();
|
|
155
178
|
if (this._updateDebounceTimer) {
|
|
156
179
|
clearTimeout(this._updateDebounceTimer);
|
|
157
180
|
}
|
|
@@ -229,25 +252,21 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
229
252
|
this._dispatchNodeSelected(null);
|
|
230
253
|
});
|
|
231
254
|
|
|
232
|
-
// Auto-fit after stabilization completes
|
|
255
|
+
// Auto-fit after initial stabilization completes (only once)
|
|
233
256
|
this._network.on("stabilizationIterationsDone", () => {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
257
|
+
if (!this._initialFitDone) {
|
|
258
|
+
// Fit with padding to keep nodes away from edges
|
|
259
|
+
this._network?.fit({
|
|
260
|
+
animation: {
|
|
261
|
+
duration: 500,
|
|
262
|
+
easingFunction: "easeInOutQuad",
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
this._initialFitDone = true;
|
|
242
266
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
animation: {
|
|
247
|
-
duration: 300,
|
|
248
|
-
easingFunction: "easeInOutQuad",
|
|
249
|
-
},
|
|
250
|
-
});
|
|
267
|
+
// Start auto-freeze timer (5 seconds after initial stabilization)
|
|
268
|
+
this._startAutoFreezeTimer();
|
|
269
|
+
}
|
|
251
270
|
});
|
|
252
271
|
|
|
253
272
|
this._updateGraph();
|
|
@@ -272,6 +291,99 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
272
291
|
});
|
|
273
292
|
}
|
|
274
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Zooms in by 20%.
|
|
296
|
+
*/
|
|
297
|
+
public zoomIn(): void {
|
|
298
|
+
if (!this._network) return;
|
|
299
|
+
const scale = this._network.getScale();
|
|
300
|
+
this._network.moveTo({
|
|
301
|
+
scale: scale * 1.2,
|
|
302
|
+
animation: {
|
|
303
|
+
duration: 200,
|
|
304
|
+
easingFunction: "easeInOutQuad",
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Zooms out by 20%.
|
|
311
|
+
*/
|
|
312
|
+
public zoomOut(): void {
|
|
313
|
+
if (!this._network) return;
|
|
314
|
+
const scale = this._network.getScale();
|
|
315
|
+
this._network.moveTo({
|
|
316
|
+
scale: scale / 1.2,
|
|
317
|
+
animation: {
|
|
318
|
+
duration: 200,
|
|
319
|
+
easingFunction: "easeInOutQuad",
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Returns whether physics simulation is currently enabled.
|
|
326
|
+
*/
|
|
327
|
+
public get physicsEnabled(): boolean {
|
|
328
|
+
return this._physicsEnabled;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Enables or disables physics simulation (node movement/settling).
|
|
333
|
+
* When disabled, nodes freeze in place; when enabled, they resume settling.
|
|
334
|
+
* @param enabled Whether to enable physics
|
|
335
|
+
* @param isManual If true, cancels any pending auto-freeze (user manually toggled)
|
|
336
|
+
*/
|
|
337
|
+
public setPhysicsEnabled(enabled: boolean, isManual = true): void {
|
|
338
|
+
// If user manually toggles, cancel auto-freeze to respect their choice
|
|
339
|
+
if (isManual) {
|
|
340
|
+
this._cancelAutoFreezeTimer();
|
|
341
|
+
this._autoFreezeApplied = true; // Prevent future auto-freeze
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
this._physicsEnabled = enabled;
|
|
345
|
+
this._network?.setOptions({
|
|
346
|
+
physics: { enabled },
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Starts the auto-freeze timer. Physics will be disabled after 5 seconds
|
|
352
|
+
* unless the user manually toggles physics or the timer is cancelled.
|
|
353
|
+
*/
|
|
354
|
+
private _startAutoFreezeTimer(): void {
|
|
355
|
+
// Don't auto-freeze if already applied or user has manually controlled physics
|
|
356
|
+
if (this._autoFreezeApplied) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
this._cancelAutoFreezeTimer();
|
|
361
|
+
this._autoFreezeTimer = setTimeout(() => {
|
|
362
|
+
if (!this._autoFreezeApplied && this._physicsEnabled) {
|
|
363
|
+
this.setPhysicsEnabled(false, false); // false = not manual, don't mark as applied
|
|
364
|
+
this._autoFreezeApplied = true;
|
|
365
|
+
// Dispatch event so parent can update UI state
|
|
366
|
+
this.dispatchEvent(
|
|
367
|
+
new CustomEvent("physics-changed", {
|
|
368
|
+
detail: { enabled: false },
|
|
369
|
+
bubbles: true,
|
|
370
|
+
composed: true,
|
|
371
|
+
}),
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}, 5000);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Cancels any pending auto-freeze timer.
|
|
379
|
+
*/
|
|
380
|
+
private _cancelAutoFreezeTimer(): void {
|
|
381
|
+
if (this._autoFreezeTimer) {
|
|
382
|
+
clearTimeout(this._autoFreezeTimer);
|
|
383
|
+
this._autoFreezeTimer = undefined;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
275
387
|
/**
|
|
276
388
|
* Selects a node by ID and focuses on it.
|
|
277
389
|
*/
|
|
@@ -309,16 +421,20 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
309
421
|
protected _highlightConnections(nodeId: number | string): void {
|
|
310
422
|
if (!this._edgesDataSet || !this._nodesDataSet) return;
|
|
311
423
|
|
|
312
|
-
// Store original colors before highlighting
|
|
313
424
|
const allEdges = this._edgesDataSet.get();
|
|
425
|
+
|
|
426
|
+
// First pass: Store original colors ONLY if not already stored.
|
|
427
|
+
// We must do this BEFORE any highlighting modifies edge colors.
|
|
428
|
+
// If edges were already highlighted (switching between nodes), the colors
|
|
429
|
+
// in the DataSet might be dimmed, so we rely on previously stored values.
|
|
314
430
|
for (const edge of allEdges) {
|
|
315
431
|
const edgeId = String(edge.id);
|
|
316
432
|
if (!this._originalEdgeColors.has(edgeId)) {
|
|
317
|
-
const colorObj = edge.color as { color: string; highlight: string };
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
});
|
|
433
|
+
const colorObj = edge.color as { color: string; highlight: string } | undefined;
|
|
434
|
+
// Extract colors, with fallbacks
|
|
435
|
+
const color = colorObj?.color ?? "#999999";
|
|
436
|
+
const highlight = colorObj?.highlight ?? color;
|
|
437
|
+
this._originalEdgeColors.set(edgeId, { color, highlight });
|
|
322
438
|
}
|
|
323
439
|
}
|
|
324
440
|
|
|
@@ -337,21 +453,19 @@ export abstract class BaseNetworkGraph extends LitElement {
|
|
|
337
453
|
}
|
|
338
454
|
}
|
|
339
455
|
|
|
340
|
-
// Update edges - make connected ones thicker
|
|
456
|
+
// Update edges - make connected ones thicker, dim non-connected ones
|
|
341
457
|
const dimmedColor = this._getDimmedEdgeColor();
|
|
342
458
|
const edgeUpdates = allEdges.map((edge: NetworkGraphEdge) => {
|
|
343
459
|
const isConnected = edge.from === nodeId || edge.to === nodeId;
|
|
344
460
|
const originalColor = this._originalEdgeColors.get(String(edge.id));
|
|
461
|
+
// Use stored original color for connected edges, fallback to a default if somehow missing
|
|
462
|
+
const connectedColor = originalColor ?? { color: "#999999", highlight: "#999999" };
|
|
345
463
|
return {
|
|
346
464
|
id: edge.id,
|
|
347
465
|
width: isConnected ? 3 : 1,
|
|
348
|
-
// Dim non-connected edges
|
|
349
466
|
color: isConnected
|
|
350
|
-
? { color:
|
|
351
|
-
: {
|
|
352
|
-
color: dimmedColor,
|
|
353
|
-
highlight: dimmedColor,
|
|
354
|
-
},
|
|
467
|
+
? { color: connectedColor.color, highlight: connectedColor.highlight }
|
|
468
|
+
: { color: dimmedColor, highlight: dimmedColor },
|
|
355
469
|
};
|
|
356
470
|
});
|
|
357
471
|
this._edgesDataSet.update(edgeUpdates);
|
|
@@ -278,6 +278,6 @@ export function createUnknownDeviceIconDataUrl(isRouter: boolean = false, isSele
|
|
|
278
278
|
* @returns A data URL containing the SVG
|
|
279
279
|
*/
|
|
280
280
|
export function createWiFiRouterIconDataUrl(isSelected: boolean = false): string {
|
|
281
|
-
const color = isSelected ? "#1976d2" : "#
|
|
281
|
+
const color = isSelected ? "#1976d2" : "#ff9800"; // Orange for external infrastructure (same as Thread unknown)
|
|
282
282
|
return createIconDataUrl(mdiWifi, color);
|
|
283
283
|
}
|