@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.
- package/dist/esm/pages/cluster-commands/base-cluster-commands.d.ts +2 -2
- package/dist/esm/pages/cluster-commands/base-cluster-commands.d.ts.map +1 -1
- package/dist/esm/pages/cluster-commands/base-cluster-commands.js.map +1 -1
- package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.d.ts +36 -0
- package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.d.ts.map +1 -0
- package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.js +159 -0
- package/dist/esm/pages/cluster-commands/clusters/basic-information-commands.js.map +6 -0
- package/dist/esm/pages/cluster-commands/index.d.ts +1 -0
- package/dist/esm/pages/cluster-commands/index.d.ts.map +1 -1
- package/dist/esm/pages/cluster-commands/index.js +1 -0
- package/dist/esm/pages/cluster-commands/index.js.map +1 -1
- package/dist/esm/pages/components/footer.d.ts.map +1 -1
- package/dist/esm/pages/components/footer.js +4 -7
- package/dist/esm/pages/components/footer.js.map +1 -1
- package/dist/esm/pages/components/header.d.ts +5 -0
- package/dist/esm/pages/components/header.d.ts.map +1 -1
- package/dist/esm/pages/components/header.js +75 -0
- package/dist/esm/pages/components/header.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/components/server-details.d.ts.map +1 -1
- package/dist/esm/pages/components/server-details.js +0 -1
- package/dist/esm/pages/components/server-details.js.map +1 -1
- package/dist/esm/pages/matter-dashboard-app.d.ts +12 -0
- package/dist/esm/pages/matter-dashboard-app.d.ts.map +1 -1
- package/dist/esm/pages/matter-dashboard-app.js +84 -4
- package/dist/esm/pages/matter-dashboard-app.js.map +1 -1
- package/dist/esm/pages/matter-network-view.d.ts +52 -0
- package/dist/esm/pages/matter-network-view.d.ts.map +1 -0
- package/dist/esm/pages/matter-network-view.js +309 -0
- package/dist/esm/pages/matter-network-view.js.map +6 -0
- package/dist/esm/pages/matter-node-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-node-view.js +70 -1
- package/dist/esm/pages/matter-node-view.js.map +1 -1
- package/dist/esm/pages/matter-server-view.d.ts +4 -0
- package/dist/esm/pages/matter-server-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-server-view.js +16 -1
- package/dist/esm/pages/matter-server-view.js.map +1 -1
- package/dist/esm/pages/network/base-network-graph.d.ts +74 -0
- package/dist/esm/pages/network/base-network-graph.d.ts.map +1 -0
- package/dist/esm/pages/network/base-network-graph.js +403 -0
- package/dist/esm/pages/network/base-network-graph.js.map +6 -0
- package/dist/esm/pages/network/device-icons.d.ts +52 -0
- package/dist/esm/pages/network/device-icons.d.ts.map +1 -0
- package/dist/esm/pages/network/device-icons.js +197 -0
- package/dist/esm/pages/network/device-icons.js.map +6 -0
- package/dist/esm/pages/network/device-panel.d.ts +31 -0
- package/dist/esm/pages/network/device-panel.d.ts.map +1 -0
- package/dist/esm/pages/network/device-panel.js +183 -0
- package/dist/esm/pages/network/device-panel.js.map +6 -0
- package/dist/esm/pages/network/network-details.d.ts +47 -0
- package/dist/esm/pages/network/network-details.d.ts.map +1 -0
- package/dist/esm/pages/network/network-details.js +686 -0
- package/dist/esm/pages/network/network-details.js.map +6 -0
- package/dist/esm/pages/network/network-types.d.ts +153 -0
- package/dist/esm/pages/network/network-types.d.ts.map +1 -0
- package/dist/esm/pages/network/network-types.js +19 -0
- package/dist/esm/pages/network/network-types.js.map +6 -0
- package/dist/esm/pages/network/network-utils.d.ts +170 -0
- package/dist/esm/pages/network/network-utils.d.ts.map +1 -0
- package/dist/esm/pages/network/network-utils.js +472 -0
- package/dist/esm/pages/network/network-utils.js.map +6 -0
- package/dist/esm/pages/network/thread-graph.d.ts +27 -0
- package/dist/esm/pages/network/thread-graph.d.ts.map +1 -0
- package/dist/esm/pages/network/thread-graph.js +134 -0
- package/dist/esm/pages/network/thread-graph.js.map +6 -0
- package/dist/esm/pages/network/wifi-graph.d.ts +27 -0
- package/dist/esm/pages/network/wifi-graph.d.ts.map +1 -0
- package/dist/esm/pages/network/wifi-graph.js +167 -0
- package/dist/esm/pages/network/wifi-graph.js.map +6 -0
- package/dist/web/js/{commission-node-dialog-CBSDiqRW.js → commission-node-dialog-B1_khzZb.js} +5 -5
- package/dist/web/js/{commission-node-existing-TP6s8Tez.js → commission-node-existing-RpdajrwF.js} +2 -5
- package/dist/web/js/{commission-node-thread-DOB8pu6x.js → commission-node-thread-5f2itkTG.js} +2 -5
- package/dist/web/js/{commission-node-wifi-tzavmk1j.js → commission-node-wifi-DZ_pWqsa.js} +2 -5
- package/dist/web/js/{dialog-box-Dknil_Be.js → dialog-box-DEUxM4B1.js} +2 -2
- package/dist/web/js/{fire_event-DRpOSjJR.js → fire_event-BczBMT8E.js} +1 -1
- package/dist/web/js/{log-level-dialog-TXkma-7Z.js → log-level-dialog-Cr3PfX1X.js} +2 -3
- package/dist/web/js/main.js +1 -1
- package/dist/web/js/matter-dashboard-app-BuCe_Jxf.js +29990 -0
- package/dist/web/js/{node-binding-dialog-D52FCBFP.js → node-binding-dialog-DMiHNDLA.js} +2 -4
- package/dist/web/js/{prevent_default-BPgSQsuY.js → prevent_default-D4FX_PIh.js} +2 -42
- package/package.json +5 -4
- package/src/pages/cluster-commands/base-cluster-commands.ts +2 -2
- package/src/pages/cluster-commands/clusters/basic-information-commands.ts +171 -0
- package/src/pages/cluster-commands/index.ts +1 -0
- package/src/pages/components/footer.ts +4 -7
- package/src/pages/components/header.ts +81 -0
- package/src/pages/components/node-details.ts +2 -2
- package/src/pages/components/server-details.ts +0 -1
- package/src/pages/matter-dashboard-app.ts +105 -5
- package/src/pages/matter-network-view.ts +325 -0
- package/src/pages/matter-node-view.ts +75 -1
- package/src/pages/matter-server-view.ts +17 -1
- package/src/pages/network/base-network-graph.ts +463 -0
- package/src/pages/network/device-icons.ts +283 -0
- package/src/pages/network/device-panel.ts +180 -0
- package/src/pages/network/network-details.ts +750 -0
- package/src/pages/network/network-types.ts +161 -0
- package/src/pages/network/network-utils.ts +752 -0
- package/src/pages/network/thread-graph.ts +164 -0
- package/src/pages/network/wifi-graph.ts +192 -0
- package/dist/web/js/matter-dashboard-app-B7GUghkC.js +0 -17254
- package/dist/web/js/outlined-text-field-D1DyKQY-.js +0 -968
- 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
|
+
}
|