@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,686 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* @license
|
|
13
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
14
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
15
|
+
*/
|
|
16
|
+
import "@material/web/divider/divider";
|
|
17
|
+
import { mdiClose, mdiSignal, mdiSignalCellular1, mdiSignalCellular2 } from "@mdi/js";
|
|
18
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
19
|
+
import { customElement, property } from "lit/decorators.js";
|
|
20
|
+
import "../../components/ha-svg-icon";
|
|
21
|
+
import {
|
|
22
|
+
buildExtAddrMap,
|
|
23
|
+
getDeviceName,
|
|
24
|
+
getNetworkType,
|
|
25
|
+
getNodeConnections,
|
|
26
|
+
getSignalColor,
|
|
27
|
+
getSignalColorFromRssi,
|
|
28
|
+
getThreadChannel,
|
|
29
|
+
getThreadExtendedAddressHex,
|
|
30
|
+
getThreadRole,
|
|
31
|
+
getThreadRoleName,
|
|
32
|
+
getWiFiDiagnostics,
|
|
33
|
+
getWiFiSecurityTypeName,
|
|
34
|
+
getWiFiVersionName,
|
|
35
|
+
parseNeighborTable
|
|
36
|
+
} from "./network-utils.js";
|
|
37
|
+
let NetworkDetails = class extends LitElement {
|
|
38
|
+
constructor() {
|
|
39
|
+
super(...arguments);
|
|
40
|
+
this.selectedNodeId = null;
|
|
41
|
+
this.nodes = {};
|
|
42
|
+
this.unknownDevices = /* @__PURE__ */ new Map();
|
|
43
|
+
this.wifiAccessPoints = /* @__PURE__ */ new Map();
|
|
44
|
+
}
|
|
45
|
+
_handleClose() {
|
|
46
|
+
this.dispatchEvent(
|
|
47
|
+
new CustomEvent("close", {
|
|
48
|
+
bubbles: true,
|
|
49
|
+
composed: true
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
_handleSelectNode(nodeId) {
|
|
54
|
+
this.dispatchEvent(
|
|
55
|
+
new CustomEvent("select-node", {
|
|
56
|
+
detail: { nodeId },
|
|
57
|
+
bubbles: true,
|
|
58
|
+
composed: true
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
/** Handle keyboard interaction for clickable elements (Enter/Space activates) */
|
|
63
|
+
_handleKeyDown(event, nodeId) {
|
|
64
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
this._handleSelectNode(nodeId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
_formatExtAddress(extAddr) {
|
|
70
|
+
if (extAddr === void 0 || extAddr === "") return "Unknown";
|
|
71
|
+
if (typeof extAddr === "bigint") {
|
|
72
|
+
return extAddr.toString(16).toUpperCase().padStart(16, "0");
|
|
73
|
+
}
|
|
74
|
+
return extAddr;
|
|
75
|
+
}
|
|
76
|
+
_getSignalIcon(neighbor) {
|
|
77
|
+
const color = getSignalColor(neighbor);
|
|
78
|
+
if (color === "#4caf50") return mdiSignal;
|
|
79
|
+
if (color === "#ff9800") return mdiSignalCellular2;
|
|
80
|
+
return mdiSignalCellular1;
|
|
81
|
+
}
|
|
82
|
+
_getSignalIconFromColor(color) {
|
|
83
|
+
if (color === "#4caf50") return mdiSignal;
|
|
84
|
+
if (color === "#ff9800") return mdiSignalCellular2;
|
|
85
|
+
return mdiSignalCellular1;
|
|
86
|
+
}
|
|
87
|
+
_renderWiFiInfo(node) {
|
|
88
|
+
const wifiDiag = getWiFiDiagnostics(node);
|
|
89
|
+
if (!wifiDiag.bssid && wifiDiag.rssi === null) {
|
|
90
|
+
return nothing;
|
|
91
|
+
}
|
|
92
|
+
const signalColor = getSignalColorFromRssi(wifiDiag.rssi);
|
|
93
|
+
return html`
|
|
94
|
+
<div class="section">
|
|
95
|
+
<h4>WiFi Network</h4>
|
|
96
|
+
${wifiDiag.bssid ? html`
|
|
97
|
+
<div class="info-row">
|
|
98
|
+
<span class="label">BSSID:</span>
|
|
99
|
+
<span class="value mono">${wifiDiag.bssid}</span>
|
|
100
|
+
</div>
|
|
101
|
+
` : nothing}
|
|
102
|
+
${wifiDiag.rssi !== null ? html`
|
|
103
|
+
<div class="info-row">
|
|
104
|
+
<span class="label">Signal:</span>
|
|
105
|
+
<span class="value" style="color: ${signalColor}">${wifiDiag.rssi} dBm</span>
|
|
106
|
+
</div>
|
|
107
|
+
` : nothing}
|
|
108
|
+
${wifiDiag.channel !== null ? html`
|
|
109
|
+
<div class="info-row">
|
|
110
|
+
<span class="label">Channel:</span>
|
|
111
|
+
<span class="value">${wifiDiag.channel}</span>
|
|
112
|
+
</div>
|
|
113
|
+
` : nothing}
|
|
114
|
+
${wifiDiag.securityType !== null ? html`
|
|
115
|
+
<div class="info-row">
|
|
116
|
+
<span class="label">Security:</span>
|
|
117
|
+
<span class="value">${getWiFiSecurityTypeName(wifiDiag.securityType)}</span>
|
|
118
|
+
</div>
|
|
119
|
+
` : nothing}
|
|
120
|
+
${wifiDiag.wifiVersion !== null ? html`
|
|
121
|
+
<div class="info-row">
|
|
122
|
+
<span class="label">WiFi Version:</span>
|
|
123
|
+
<span class="value">${getWiFiVersionName(wifiDiag.wifiVersion)}</span>
|
|
124
|
+
</div>
|
|
125
|
+
` : nothing}
|
|
126
|
+
</div>
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
_renderThreadInfo(node) {
|
|
130
|
+
const threadRole = getThreadRole(node);
|
|
131
|
+
const channel = getThreadChannel(node);
|
|
132
|
+
const extAddressHex = getThreadExtendedAddressHex(node);
|
|
133
|
+
const extAddrMap = buildExtAddrMap(this.nodes);
|
|
134
|
+
const nodeId = String(node.node_id);
|
|
135
|
+
const connections = getNodeConnections(nodeId, this.nodes, extAddrMap);
|
|
136
|
+
return html`
|
|
137
|
+
<div class="section">
|
|
138
|
+
<h4>Thread Network</h4>
|
|
139
|
+
<div class="info-row">
|
|
140
|
+
<span class="label">Role:</span>
|
|
141
|
+
<span class="value">${getThreadRoleName(threadRole)}</span>
|
|
142
|
+
</div>
|
|
143
|
+
${channel !== void 0 ? html`
|
|
144
|
+
<div class="info-row">
|
|
145
|
+
<span class="label">Channel:</span>
|
|
146
|
+
<span class="value">${channel}</span>
|
|
147
|
+
</div>
|
|
148
|
+
` : nothing}
|
|
149
|
+
${extAddressHex ? html`
|
|
150
|
+
<div class="info-row">
|
|
151
|
+
<span class="label">Extended Address:</span>
|
|
152
|
+
<span class="value mono">${extAddressHex}</span>
|
|
153
|
+
</div>
|
|
154
|
+
` : nothing}
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
${connections.length > 0 ? html`
|
|
158
|
+
<md-divider></md-divider>
|
|
159
|
+
<div class="section">
|
|
160
|
+
<h4>Connections (${connections.length})</h4>
|
|
161
|
+
<div class="neighbors-list">
|
|
162
|
+
${connections.map((conn) => {
|
|
163
|
+
return html`
|
|
164
|
+
<div
|
|
165
|
+
class="neighbor-item clickable"
|
|
166
|
+
role="button"
|
|
167
|
+
tabindex="0"
|
|
168
|
+
@click=${() => this._handleSelectNode(conn.connectedNodeId)}
|
|
169
|
+
@keydown=${(e) => this._handleKeyDown(e, conn.connectedNodeId)}
|
|
170
|
+
>
|
|
171
|
+
<ha-svg-icon
|
|
172
|
+
.path=${this._getSignalIconFromColor(conn.signalColor)}
|
|
173
|
+
style="--icon-primary-color: ${conn.signalColor}"
|
|
174
|
+
></ha-svg-icon>
|
|
175
|
+
<div class="neighbor-info">
|
|
176
|
+
<div class="neighbor-name">
|
|
177
|
+
${conn.connectedNode ? html`Node ${conn.connectedNodeId}:
|
|
178
|
+
${getDeviceName(conn.connectedNode)}` : html`External: <span class="mono">${conn.extAddressHex}</span>`}
|
|
179
|
+
</div>
|
|
180
|
+
<div class="neighbor-signal">
|
|
181
|
+
${conn.rssi !== null ? html`RSSI: ${conn.rssi} dBm, ` : nothing}
|
|
182
|
+
${conn.lqi !== null ? html`LQI: ${conn.lqi}` : nothing}
|
|
183
|
+
${!conn.isOutgoing ? html`<span class="direction-hint">(reverse)</span>` : nothing}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
`;
|
|
188
|
+
})}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
` : nothing}
|
|
192
|
+
`;
|
|
193
|
+
}
|
|
194
|
+
_renderNodeInfo(node) {
|
|
195
|
+
const networkType = getNetworkType(node);
|
|
196
|
+
return html`
|
|
197
|
+
<div class="section">
|
|
198
|
+
<h4>Device Info</h4>
|
|
199
|
+
<div class="info-row">
|
|
200
|
+
<span class="label">Name:</span>
|
|
201
|
+
<span class="value">${getDeviceName(node)}</span>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="info-row">
|
|
204
|
+
<span class="label">Vendor:</span>
|
|
205
|
+
<span class="value">${node.vendorName ?? "Unknown"}</span>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="info-row">
|
|
208
|
+
<span class="label">Product:</span>
|
|
209
|
+
<span class="value">${node.productName ?? "Unknown"}</span>
|
|
210
|
+
</div>
|
|
211
|
+
${node.serialNumber ? html`
|
|
212
|
+
<div class="info-row">
|
|
213
|
+
<span class="label">Serial:</span>
|
|
214
|
+
<span class="value mono">${node.serialNumber}</span>
|
|
215
|
+
</div>
|
|
216
|
+
` : nothing}
|
|
217
|
+
<div class="info-row">
|
|
218
|
+
<span class="label">Network:</span>
|
|
219
|
+
<span class="value">${networkType.charAt(0).toUpperCase() + networkType.slice(1)}</span>
|
|
220
|
+
</div>
|
|
221
|
+
<div class="info-row">
|
|
222
|
+
<span class="label">Status:</span>
|
|
223
|
+
<span class="value ${node.available ? "status-online" : "status-offline"}"
|
|
224
|
+
>${node.available ? "Online" : "Offline"}</span
|
|
225
|
+
>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
${networkType === "thread" ? html`
|
|
230
|
+
<md-divider></md-divider>
|
|
231
|
+
${this._renderThreadInfo(node)}
|
|
232
|
+
` : nothing}
|
|
233
|
+
${networkType === "wifi" ? html`
|
|
234
|
+
<md-divider></md-divider>
|
|
235
|
+
${this._renderWiFiInfo(node)}
|
|
236
|
+
` : nothing}
|
|
237
|
+
`;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Find the neighbor entry for an unknown device from a node's neighbor table.
|
|
241
|
+
*/
|
|
242
|
+
_findNeighborEntry(node, unknownExtAddrHex) {
|
|
243
|
+
const neighbors = parseNeighborTable(node);
|
|
244
|
+
for (const neighbor of neighbors) {
|
|
245
|
+
const neighborHex = this._formatExtAddress(neighbor.extAddress);
|
|
246
|
+
if (neighborHex === unknownExtAddrHex) {
|
|
247
|
+
return neighbor;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
_renderUnknownDeviceInfo(deviceId) {
|
|
253
|
+
const unknown = this.unknownDevices.get(deviceId);
|
|
254
|
+
if (!unknown) {
|
|
255
|
+
return html`<p>Unknown device data not available</p>`;
|
|
256
|
+
}
|
|
257
|
+
return html`
|
|
258
|
+
<div class="section">
|
|
259
|
+
<h4>Unknown Device</h4>
|
|
260
|
+
<div class="info-row">
|
|
261
|
+
<span class="label">Type:</span>
|
|
262
|
+
<span class="value">${unknown.isRouter ? "Router (external)" : "End Device (external)"}</span>
|
|
263
|
+
</div>
|
|
264
|
+
<div class="info-row">
|
|
265
|
+
<span class="label">Extended Address:</span>
|
|
266
|
+
<span class="value mono">${unknown.extAddressHex}</span>
|
|
267
|
+
</div>
|
|
268
|
+
${unknown.bestRssi !== null ? html`
|
|
269
|
+
<div class="info-row">
|
|
270
|
+
<span class="label">Best RSSI:</span>
|
|
271
|
+
<span class="value">${unknown.bestRssi} dBm</span>
|
|
272
|
+
</div>
|
|
273
|
+
` : nothing}
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
${unknown.seenBy.length > 0 ? html`
|
|
277
|
+
<md-divider></md-divider>
|
|
278
|
+
<div class="section">
|
|
279
|
+
<h4>Neighbors (${unknown.seenBy.length})</h4>
|
|
280
|
+
<div class="neighbors-list">
|
|
281
|
+
${unknown.seenBy.map((nodeId) => {
|
|
282
|
+
const node = this.nodes[nodeId.toString()];
|
|
283
|
+
if (!node) return nothing;
|
|
284
|
+
const neighborEntry = this._findNeighborEntry(node, unknown.extAddressHex);
|
|
285
|
+
const signalColor = neighborEntry ? getSignalColor(neighborEntry) : "#999";
|
|
286
|
+
const rssi = neighborEntry?.avgRssi ?? neighborEntry?.lastRssi ?? null;
|
|
287
|
+
const lqi = neighborEntry?.lqi;
|
|
288
|
+
return html`
|
|
289
|
+
<div
|
|
290
|
+
class="neighbor-item clickable"
|
|
291
|
+
role="button"
|
|
292
|
+
tabindex="0"
|
|
293
|
+
@click=${() => this._handleSelectNode(nodeId)}
|
|
294
|
+
@keydown=${(e) => this._handleKeyDown(e, nodeId)}
|
|
295
|
+
>
|
|
296
|
+
${neighborEntry ? html`
|
|
297
|
+
<ha-svg-icon
|
|
298
|
+
.path=${this._getSignalIcon(neighborEntry)}
|
|
299
|
+
style="--icon-primary-color: ${signalColor}"
|
|
300
|
+
></ha-svg-icon>
|
|
301
|
+
` : nothing}
|
|
302
|
+
<div class="neighbor-info">
|
|
303
|
+
<div class="neighbor-name">Node ${nodeId}: ${getDeviceName(node)}</div>
|
|
304
|
+
${neighborEntry ? html`
|
|
305
|
+
<div class="neighbor-signal">
|
|
306
|
+
${rssi !== null ? html`RSSI: ${rssi} dBm, ` : nothing}
|
|
307
|
+
${lqi !== void 0 ? html`LQI: ${lqi}` : nothing}
|
|
308
|
+
</div>
|
|
309
|
+
` : nothing}
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
`;
|
|
313
|
+
})}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
` : nothing}
|
|
317
|
+
|
|
318
|
+
<md-divider></md-divider>
|
|
319
|
+
<div class="section">
|
|
320
|
+
<p class="hint-text">
|
|
321
|
+
This device appears in Thread neighbor tables but is not commissioned to this fabric. It may be a
|
|
322
|
+
Thread Border Router or a device from another Matter ecosystem.
|
|
323
|
+
</p>
|
|
324
|
+
</div>
|
|
325
|
+
`;
|
|
326
|
+
}
|
|
327
|
+
_renderWiFiAccessPointInfo(apId) {
|
|
328
|
+
const ap = this.wifiAccessPoints.get(apId);
|
|
329
|
+
if (!ap) {
|
|
330
|
+
return html`<p>Access point data not available</p>`;
|
|
331
|
+
}
|
|
332
|
+
return html`
|
|
333
|
+
<div class="section">
|
|
334
|
+
<h4>WiFi Access Point</h4>
|
|
335
|
+
<div class="info-row">
|
|
336
|
+
<span class="label">BSSID:</span>
|
|
337
|
+
<span class="value mono">${ap.bssid}</span>
|
|
338
|
+
</div>
|
|
339
|
+
<div class="info-row">
|
|
340
|
+
<span class="label">Connected devices:</span>
|
|
341
|
+
<span class="value">${ap.connectedNodes.length}</span>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
${ap.connectedNodes.length > 0 ? html`
|
|
345
|
+
<md-divider></md-divider>
|
|
346
|
+
<div class="section">
|
|
347
|
+
<h4>Connected Nodes</h4>
|
|
348
|
+
<div class="connected-nodes-list">
|
|
349
|
+
${ap.connectedNodes.map((nodeId) => {
|
|
350
|
+
const node = this.nodes[nodeId.toString()];
|
|
351
|
+
if (!node) return nothing;
|
|
352
|
+
const wifiDiag = getWiFiDiagnostics(node);
|
|
353
|
+
const signalColor = getSignalColorFromRssi(wifiDiag.rssi);
|
|
354
|
+
return html`
|
|
355
|
+
<div
|
|
356
|
+
class="connected-node-item clickable"
|
|
357
|
+
role="button"
|
|
358
|
+
tabindex="0"
|
|
359
|
+
@click=${() => this._handleSelectNode(nodeId)}
|
|
360
|
+
@keydown=${(e) => this._handleKeyDown(e, nodeId)}
|
|
361
|
+
>
|
|
362
|
+
<div class="node-name">Node ${nodeId}: ${getDeviceName(node)}</div>
|
|
363
|
+
${wifiDiag.rssi !== null ? html`<div class="node-signal" style="color: ${signalColor}">
|
|
364
|
+
${wifiDiag.rssi} dBm
|
|
365
|
+
</div>` : nothing}
|
|
366
|
+
</div>
|
|
367
|
+
`;
|
|
368
|
+
})}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
` : nothing}
|
|
372
|
+
<md-divider></md-divider>
|
|
373
|
+
<div class="section">
|
|
374
|
+
<p class="hint-text">
|
|
375
|
+
This is a WiFi access point that Matter devices connect to. It is not a Matter device itself.
|
|
376
|
+
</p>
|
|
377
|
+
</div>
|
|
378
|
+
`;
|
|
379
|
+
}
|
|
380
|
+
render() {
|
|
381
|
+
if (this.selectedNodeId === null) {
|
|
382
|
+
return html`
|
|
383
|
+
<div class="empty-state">
|
|
384
|
+
<p>Select a device to view details</p>
|
|
385
|
+
</div>
|
|
386
|
+
`;
|
|
387
|
+
}
|
|
388
|
+
const isUnknown = typeof this.selectedNodeId === "string" && this.selectedNodeId.startsWith("unknown_");
|
|
389
|
+
if (isUnknown) {
|
|
390
|
+
return html`
|
|
391
|
+
<div class="details-panel">
|
|
392
|
+
<div class="header">
|
|
393
|
+
<h3>External Device</h3>
|
|
394
|
+
<button class="close-button" @click=${this._handleClose} aria-label="Close details panel">
|
|
395
|
+
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
|
396
|
+
</button>
|
|
397
|
+
</div>
|
|
398
|
+
<div class="content">${this._renderUnknownDeviceInfo(this.selectedNodeId)}</div>
|
|
399
|
+
</div>
|
|
400
|
+
`;
|
|
401
|
+
}
|
|
402
|
+
const isAccessPoint = typeof this.selectedNodeId === "string" && this.selectedNodeId.startsWith("ap_");
|
|
403
|
+
if (isAccessPoint) {
|
|
404
|
+
return html`
|
|
405
|
+
<div class="details-panel">
|
|
406
|
+
<div class="header">
|
|
407
|
+
<h3>Access Point</h3>
|
|
408
|
+
<button class="close-button" @click=${this._handleClose} aria-label="Close details panel">
|
|
409
|
+
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
|
410
|
+
</button>
|
|
411
|
+
</div>
|
|
412
|
+
<div class="content">${this._renderWiFiAccessPointInfo(this.selectedNodeId)}</div>
|
|
413
|
+
</div>
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
const node = this.nodes[this.selectedNodeId.toString()];
|
|
417
|
+
if (!node) {
|
|
418
|
+
return html`
|
|
419
|
+
<div class="empty-state">
|
|
420
|
+
<p>Device not found</p>
|
|
421
|
+
</div>
|
|
422
|
+
`;
|
|
423
|
+
}
|
|
424
|
+
return html`
|
|
425
|
+
<div class="details-panel">
|
|
426
|
+
<div class="header">
|
|
427
|
+
<h3>Node ${this.selectedNodeId}</h3>
|
|
428
|
+
<button class="close-button" @click=${this._handleClose} aria-label="Close details panel">
|
|
429
|
+
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
|
430
|
+
</button>
|
|
431
|
+
</div>
|
|
432
|
+
<div class="content">${this._renderNodeInfo(node)}</div>
|
|
433
|
+
<div class="footer">
|
|
434
|
+
<a href="#node/${this.selectedNodeId}" class="view-link">View node details</a>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
`;
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
NetworkDetails.styles = css`
|
|
441
|
+
:host {
|
|
442
|
+
display: block;
|
|
443
|
+
height: 100%;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.empty-state {
|
|
447
|
+
display: flex;
|
|
448
|
+
align-items: center;
|
|
449
|
+
justify-content: center;
|
|
450
|
+
height: 100%;
|
|
451
|
+
color: var(--md-sys-color-on-surface-variant, #666);
|
|
452
|
+
text-align: center;
|
|
453
|
+
padding: 24px;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.details-panel {
|
|
457
|
+
display: flex;
|
|
458
|
+
flex-direction: column;
|
|
459
|
+
height: 100%;
|
|
460
|
+
background-color: var(--md-sys-color-surface, #fff);
|
|
461
|
+
border-radius: 8px;
|
|
462
|
+
border: 1px solid var(--md-sys-color-outline-variant, #ccc);
|
|
463
|
+
overflow: hidden;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.header {
|
|
467
|
+
display: flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
justify-content: space-between;
|
|
470
|
+
padding: 12px 16px;
|
|
471
|
+
background-color: var(--md-sys-color-surface-container, #f5f5f5);
|
|
472
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, #ccc);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.header h3 {
|
|
476
|
+
margin: 0;
|
|
477
|
+
font-size: 1rem;
|
|
478
|
+
font-weight: 500;
|
|
479
|
+
color: var(--md-sys-color-on-surface, #333);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.close-button {
|
|
483
|
+
background: none;
|
|
484
|
+
border: none;
|
|
485
|
+
padding: 4px;
|
|
486
|
+
cursor: pointer;
|
|
487
|
+
border-radius: 50%;
|
|
488
|
+
display: flex;
|
|
489
|
+
align-items: center;
|
|
490
|
+
justify-content: center;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.close-button:hover {
|
|
494
|
+
background-color: var(--md-sys-color-surface-container-high, #e8e8e8);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.close-button ha-svg-icon {
|
|
498
|
+
--icon-primary-color: var(--md-sys-color-on-surface-variant, #666);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.content {
|
|
502
|
+
flex: 1;
|
|
503
|
+
overflow-y: auto;
|
|
504
|
+
padding: 0;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.section {
|
|
508
|
+
padding: 16px;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.section h4 {
|
|
512
|
+
margin: 0 0 12px 0;
|
|
513
|
+
font-size: 0.875rem;
|
|
514
|
+
font-weight: 500;
|
|
515
|
+
color: var(--md-sys-color-primary, #6200ee);
|
|
516
|
+
text-transform: uppercase;
|
|
517
|
+
letter-spacing: 0.5px;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.info-row {
|
|
521
|
+
display: flex;
|
|
522
|
+
justify-content: space-between;
|
|
523
|
+
padding: 6px 0;
|
|
524
|
+
font-size: 0.875rem;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.label {
|
|
528
|
+
color: var(--md-sys-color-on-surface-variant, #666);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.value {
|
|
532
|
+
color: var(--md-sys-color-on-surface, #333);
|
|
533
|
+
font-weight: 500;
|
|
534
|
+
text-align: right;
|
|
535
|
+
word-break: break-all;
|
|
536
|
+
max-width: 60%;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.value.mono {
|
|
540
|
+
font-family: monospace;
|
|
541
|
+
font-size: 0.8rem;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.status-online {
|
|
545
|
+
color: #4caf50;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.status-offline {
|
|
549
|
+
color: var(--danger-color, #f44336);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.neighbors-list {
|
|
553
|
+
display: flex;
|
|
554
|
+
flex-direction: column;
|
|
555
|
+
gap: 8px;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.neighbor-item {
|
|
559
|
+
display: flex;
|
|
560
|
+
align-items: flex-start;
|
|
561
|
+
gap: 12px;
|
|
562
|
+
padding: 8px;
|
|
563
|
+
background-color: var(--md-sys-color-surface-container, #f5f5f5);
|
|
564
|
+
border-radius: 4px;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.neighbor-item.clickable {
|
|
568
|
+
cursor: pointer;
|
|
569
|
+
transition: background-color 0.15s;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.neighbor-item.clickable:hover {
|
|
573
|
+
background-color: var(--md-sys-color-surface-container-high, #e8e8e8);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.neighbor-item ha-svg-icon {
|
|
577
|
+
flex-shrink: 0;
|
|
578
|
+
margin-top: 2px;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.neighbor-info {
|
|
582
|
+
flex: 1;
|
|
583
|
+
min-width: 0;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.neighbor-name {
|
|
587
|
+
font-size: 0.875rem;
|
|
588
|
+
color: var(--md-sys-color-on-surface, #333);
|
|
589
|
+
word-break: break-word;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.neighbor-signal {
|
|
593
|
+
font-size: 0.75rem;
|
|
594
|
+
color: var(--md-sys-color-on-surface-variant, #666);
|
|
595
|
+
margin-top: 2px;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.direction-hint {
|
|
599
|
+
font-style: italic;
|
|
600
|
+
opacity: 0.8;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.footer {
|
|
604
|
+
padding: 12px 16px;
|
|
605
|
+
border-top: 1px solid var(--md-sys-color-outline-variant, #ccc);
|
|
606
|
+
text-align: center;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.view-link {
|
|
610
|
+
color: var(--md-sys-color-primary, #6200ee);
|
|
611
|
+
text-decoration: none;
|
|
612
|
+
font-size: 0.875rem;
|
|
613
|
+
font-weight: 500;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.view-link:hover {
|
|
617
|
+
text-decoration: underline;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
md-divider {
|
|
621
|
+
--md-divider-color: var(--md-sys-color-outline-variant, #ccc);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.hint-text {
|
|
625
|
+
font-size: 0.8rem;
|
|
626
|
+
color: var(--md-sys-color-on-surface-variant, #666);
|
|
627
|
+
line-height: 1.4;
|
|
628
|
+
margin: 0;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.connected-nodes-list {
|
|
632
|
+
display: flex;
|
|
633
|
+
flex-direction: column;
|
|
634
|
+
gap: 8px;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.connected-node-item {
|
|
638
|
+
display: flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
justify-content: space-between;
|
|
641
|
+
padding: 8px;
|
|
642
|
+
background-color: var(--md-sys-color-surface-container, #f5f5f5);
|
|
643
|
+
border-radius: 4px;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.connected-node-item.clickable {
|
|
647
|
+
cursor: pointer;
|
|
648
|
+
transition: background-color 0.15s;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.connected-node-item.clickable:hover {
|
|
652
|
+
background-color: var(--md-sys-color-surface-container-high, #e8e8e8);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.connected-node-item .node-name {
|
|
656
|
+
font-size: 0.875rem;
|
|
657
|
+
color: var(--md-sys-color-on-surface, #333);
|
|
658
|
+
word-break: break-word;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.connected-node-item .node-signal {
|
|
662
|
+
font-size: 0.8rem;
|
|
663
|
+
font-weight: 500;
|
|
664
|
+
flex-shrink: 0;
|
|
665
|
+
margin-left: 8px;
|
|
666
|
+
}
|
|
667
|
+
`;
|
|
668
|
+
__decorateClass([
|
|
669
|
+
property()
|
|
670
|
+
], NetworkDetails.prototype, "selectedNodeId", 2);
|
|
671
|
+
__decorateClass([
|
|
672
|
+
property({ type: Object })
|
|
673
|
+
], NetworkDetails.prototype, "nodes", 2);
|
|
674
|
+
__decorateClass([
|
|
675
|
+
property({ type: Object })
|
|
676
|
+
], NetworkDetails.prototype, "unknownDevices", 2);
|
|
677
|
+
__decorateClass([
|
|
678
|
+
property({ type: Object })
|
|
679
|
+
], NetworkDetails.prototype, "wifiAccessPoints", 2);
|
|
680
|
+
NetworkDetails = __decorateClass([
|
|
681
|
+
customElement("network-details")
|
|
682
|
+
], NetworkDetails);
|
|
683
|
+
export {
|
|
684
|
+
NetworkDetails
|
|
685
|
+
};
|
|
686
|
+
//# sourceMappingURL=network-details.js.map
|