@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,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { MatterClient, MatterNode } from "@matter-server/ws-client";
|
|
8
|
+
import { mdiFitToScreen } from "@mdi/js";
|
|
9
|
+
import { css, html, LitElement } from "lit";
|
|
10
|
+
import { customElement, property, query, state } from "lit/decorators.js";
|
|
11
|
+
import "../components/ha-svg-icon";
|
|
12
|
+
import "./components/footer";
|
|
13
|
+
import "./components/header";
|
|
14
|
+
import type { ActiveView } from "./components/header.js";
|
|
15
|
+
import "./network/device-panel";
|
|
16
|
+
import "./network/network-details";
|
|
17
|
+
import "./network/thread-graph";
|
|
18
|
+
import type { ThreadGraph } from "./network/thread-graph.js";
|
|
19
|
+
import "./network/wifi-graph";
|
|
20
|
+
import type { WiFiGraph } from "./network/wifi-graph.js";
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
interface HTMLElementTagNameMap {
|
|
24
|
+
"matter-network-view": MatterNetworkView;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@customElement("matter-network-view")
|
|
29
|
+
class MatterNetworkView extends LitElement {
|
|
30
|
+
public client!: MatterClient;
|
|
31
|
+
|
|
32
|
+
@property({ type: Object })
|
|
33
|
+
public nodes: Record<string, MatterNode> = {};
|
|
34
|
+
|
|
35
|
+
@property()
|
|
36
|
+
public activeView?: ActiveView;
|
|
37
|
+
|
|
38
|
+
@property()
|
|
39
|
+
public networkType: "thread" | "wifi" = "thread";
|
|
40
|
+
|
|
41
|
+
/** Initial selected node ID from URL (string to avoid BigInt precision loss) */
|
|
42
|
+
@property()
|
|
43
|
+
public initialSelectedNodeId: string | null = null;
|
|
44
|
+
|
|
45
|
+
@property({ type: Boolean })
|
|
46
|
+
public hasThreadDevices?: boolean;
|
|
47
|
+
|
|
48
|
+
@property({ type: Boolean })
|
|
49
|
+
public hasWifiDevices?: boolean;
|
|
50
|
+
|
|
51
|
+
@state()
|
|
52
|
+
private _selectedNodeId: number | string | null = null;
|
|
53
|
+
|
|
54
|
+
private _initialSelectionApplied = false;
|
|
55
|
+
private _selectRetryTimer?: ReturnType<typeof setTimeout>;
|
|
56
|
+
|
|
57
|
+
@query("thread-graph")
|
|
58
|
+
private _threadGraph?: ThreadGraph;
|
|
59
|
+
|
|
60
|
+
@query("wifi-graph")
|
|
61
|
+
private _wifiGraph?: WiFiGraph;
|
|
62
|
+
|
|
63
|
+
override willUpdate(changedProperties: Map<string, unknown>): void {
|
|
64
|
+
// Apply initial selection when the property changes
|
|
65
|
+
if (changedProperties.has("initialSelectedNodeId") && this.initialSelectedNodeId !== null) {
|
|
66
|
+
this._selectedNodeId = this.initialSelectedNodeId;
|
|
67
|
+
this._initialSelectionApplied = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
override updated(changedProperties: Map<string, unknown>): void {
|
|
72
|
+
super.updated(changedProperties);
|
|
73
|
+
|
|
74
|
+
// After render, select the node in the graph
|
|
75
|
+
if (!this._initialSelectionApplied && this.initialSelectedNodeId !== null) {
|
|
76
|
+
this._initialSelectionApplied = true;
|
|
77
|
+
// Wait for the graph to be ready, then select and focus
|
|
78
|
+
this._selectNodeWhenReady(this.initialSelectedNodeId);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override disconnectedCallback(): void {
|
|
83
|
+
super.disconnectedCallback();
|
|
84
|
+
if (this._selectRetryTimer) {
|
|
85
|
+
clearTimeout(this._selectRetryTimer);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Tries to select a node in the graph, retrying until the graph is ready.
|
|
91
|
+
*/
|
|
92
|
+
private _selectNodeWhenReady(nodeId: string | number, retries: number = 10): void {
|
|
93
|
+
const graph = this.networkType === "thread" ? this._threadGraph : this._wifiGraph;
|
|
94
|
+
|
|
95
|
+
if (graph?.isReady()) {
|
|
96
|
+
graph.selectNode(nodeId);
|
|
97
|
+
} else if (retries > 0) {
|
|
98
|
+
// Graph not ready yet, retry after a short delay
|
|
99
|
+
this._selectRetryTimer = setTimeout(() => this._selectNodeWhenReady(nodeId, retries - 1), 100);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private _handleNodeSelected(event: CustomEvent<{ nodeId: number | string | null }>): void {
|
|
104
|
+
this._selectedNodeId = event.detail.nodeId;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private _handleDetailsClose(): void {
|
|
108
|
+
this._selectedNodeId = null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private _handleSelectNode(event: CustomEvent<{ nodeId: number | string }>): void {
|
|
112
|
+
const nodeId = event.detail.nodeId;
|
|
113
|
+
this._selectedNodeId = nodeId;
|
|
114
|
+
// Select the node in the graph
|
|
115
|
+
if (this.networkType === "thread") {
|
|
116
|
+
this._threadGraph?.selectNode(nodeId);
|
|
117
|
+
} else {
|
|
118
|
+
this._wifiGraph?.selectNode(nodeId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private _handleFitToScreen(): void {
|
|
123
|
+
if (this.networkType === "thread") {
|
|
124
|
+
this._threadGraph?.fit();
|
|
125
|
+
} else {
|
|
126
|
+
this._wifiGraph?.fit();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private _renderThreadView() {
|
|
131
|
+
return html`
|
|
132
|
+
<div class="graph-section">
|
|
133
|
+
<div class="graph-header">
|
|
134
|
+
<h2>Thread Network Mesh</h2>
|
|
135
|
+
<button class="fit-button" @click=${this._handleFitToScreen} title="Fit to screen">
|
|
136
|
+
<ha-svg-icon .path=${mdiFitToScreen}></ha-svg-icon>
|
|
137
|
+
</button>
|
|
138
|
+
</div>
|
|
139
|
+
<thread-graph .nodes=${this.nodes} @node-selected=${this._handleNodeSelected}></thread-graph>
|
|
140
|
+
</div>
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private _renderWifiView() {
|
|
145
|
+
return html`
|
|
146
|
+
<div class="graph-section">
|
|
147
|
+
<div class="graph-header">
|
|
148
|
+
<h2>WiFi Network</h2>
|
|
149
|
+
<button class="fit-button" @click=${this._handleFitToScreen} title="Fit to screen">
|
|
150
|
+
<ha-svg-icon .path=${mdiFitToScreen}></ha-svg-icon>
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
<wifi-graph .nodes=${this.nodes} @node-selected=${this._handleNodeSelected}></wifi-graph>
|
|
154
|
+
</div>
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
override render() {
|
|
159
|
+
const showSidebar = this._selectedNodeId !== null;
|
|
160
|
+
const unknownDevices = this._threadGraph?.unknownDevicesMap ?? new Map();
|
|
161
|
+
const wifiAccessPoints = this._wifiGraph?.wifiAccessPointsMap ?? new Map();
|
|
162
|
+
|
|
163
|
+
return html`
|
|
164
|
+
<dashboard-header
|
|
165
|
+
title="Open Home Foundation Matter Server"
|
|
166
|
+
.client=${this.client}
|
|
167
|
+
.activeView=${this.activeView}
|
|
168
|
+
.hasThreadDevices=${this.hasThreadDevices}
|
|
169
|
+
.hasWifiDevices=${this.hasWifiDevices}
|
|
170
|
+
></dashboard-header>
|
|
171
|
+
|
|
172
|
+
<div class="content">
|
|
173
|
+
<div class="main-area">
|
|
174
|
+
${this.networkType === "thread" ? this._renderThreadView() : this._renderWifiView()}
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<aside class="details-sidebar ${showSidebar ? "visible" : ""}">
|
|
178
|
+
<network-details
|
|
179
|
+
.selectedNodeId=${this._selectedNodeId}
|
|
180
|
+
.nodes=${this.nodes}
|
|
181
|
+
.unknownDevices=${unknownDevices}
|
|
182
|
+
.wifiAccessPoints=${wifiAccessPoints}
|
|
183
|
+
@close=${this._handleDetailsClose}
|
|
184
|
+
@select-node=${this._handleSelectNode}
|
|
185
|
+
></network-details>
|
|
186
|
+
</aside>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<dashboard-footer></dashboard-footer>
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static override styles = css`
|
|
194
|
+
:host {
|
|
195
|
+
display: flex;
|
|
196
|
+
flex-direction: column;
|
|
197
|
+
height: 100vh;
|
|
198
|
+
height: 100dvh; /* dynamic viewport height - fallback above for older browsers */
|
|
199
|
+
overflow: hidden;
|
|
200
|
+
background-color: var(--md-sys-color-background, #fafafa);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.content {
|
|
204
|
+
display: flex;
|
|
205
|
+
flex: 1 1 0;
|
|
206
|
+
padding: 8px 16px;
|
|
207
|
+
gap: 8px;
|
|
208
|
+
max-width: 1600px;
|
|
209
|
+
margin: 0 auto;
|
|
210
|
+
width: 100%;
|
|
211
|
+
box-sizing: border-box;
|
|
212
|
+
min-height: 0;
|
|
213
|
+
overflow: hidden;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.main-area {
|
|
217
|
+
flex: 1 1 0;
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
min-width: 0;
|
|
221
|
+
min-height: 0;
|
|
222
|
+
overflow: hidden;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.graph-section {
|
|
226
|
+
flex: 1 1 0;
|
|
227
|
+
min-height: 0;
|
|
228
|
+
display: flex;
|
|
229
|
+
flex-direction: column;
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.graph-header {
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
justify-content: space-between;
|
|
237
|
+
margin-bottom: 4px;
|
|
238
|
+
flex-shrink: 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.graph-header h2 {
|
|
242
|
+
margin: 0;
|
|
243
|
+
font-size: 1.1rem;
|
|
244
|
+
font-weight: 500;
|
|
245
|
+
color: var(--md-sys-color-on-background, #333);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.fit-button {
|
|
249
|
+
background: none;
|
|
250
|
+
border: 1px solid var(--md-sys-color-outline-variant, #ccc);
|
|
251
|
+
border-radius: 4px;
|
|
252
|
+
padding: 6px;
|
|
253
|
+
cursor: pointer;
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
justify-content: center;
|
|
257
|
+
transition: background-color 0.2s;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.fit-button:hover {
|
|
261
|
+
background-color: var(--md-sys-color-surface-container-high, #e8e8e8);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.fit-button ha-svg-icon {
|
|
265
|
+
--icon-primary-color: var(--md-sys-color-on-surface-variant, #666);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.graph-section thread-graph,
|
|
269
|
+
.graph-section wifi-graph {
|
|
270
|
+
flex: 1 1 0;
|
|
271
|
+
min-height: 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.wifi-section {
|
|
275
|
+
display: flex;
|
|
276
|
+
flex-direction: column;
|
|
277
|
+
gap: 16px;
|
|
278
|
+
overflow-y: auto;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.wifi-section h2 {
|
|
282
|
+
margin: 0;
|
|
283
|
+
font-size: 1.25rem;
|
|
284
|
+
font-weight: 500;
|
|
285
|
+
color: var(--md-sys-color-on-background, #333);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.wifi-section device-panel {
|
|
289
|
+
flex-shrink: 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.details-sidebar {
|
|
293
|
+
width: 320px;
|
|
294
|
+
flex-shrink: 0;
|
|
295
|
+
display: none;
|
|
296
|
+
min-height: 0;
|
|
297
|
+
overflow-y: auto;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.details-sidebar.visible {
|
|
301
|
+
display: block;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@media (max-width: 1024px) {
|
|
305
|
+
.content {
|
|
306
|
+
flex-direction: column;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.details-sidebar {
|
|
310
|
+
width: 100%;
|
|
311
|
+
max-height: 300px;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.details-sidebar.visible {
|
|
315
|
+
display: block;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@media (max-width: 600px) {
|
|
320
|
+
.content {
|
|
321
|
+
padding: 8px;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
`;
|
|
325
|
+
}
|
|
@@ -9,7 +9,7 @@ import "@material/web/iconbutton/icon-button";
|
|
|
9
9
|
import "@material/web/list/list";
|
|
10
10
|
import "@material/web/list/list-item";
|
|
11
11
|
import { MatterClient, MatterNode } from "@matter-server/ws-client";
|
|
12
|
-
import { mdiChevronRight } from "@mdi/js";
|
|
12
|
+
import { mdiChevronRight, mdiGraphOutline } from "@mdi/js";
|
|
13
13
|
import { LitElement, css, html } from "lit";
|
|
14
14
|
import { customElement, property } from "lit/decorators.js";
|
|
15
15
|
import { guard } from "lit/directives/guard.js";
|
|
@@ -17,6 +17,7 @@ import "../components/ha-svg-icon";
|
|
|
17
17
|
import "./components/header";
|
|
18
18
|
import "./components/node-details";
|
|
19
19
|
import { getEndpointDeviceTypes } from "./matter-endpoint-view.js";
|
|
20
|
+
import { getNetworkType } from "./network/network-utils.js";
|
|
20
21
|
|
|
21
22
|
declare global {
|
|
22
23
|
interface HTMLElementTagNameMap {
|
|
@@ -46,6 +47,13 @@ class MatterNodeView extends LitElement {
|
|
|
46
47
|
`;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
const networkType = getNetworkType(this.node);
|
|
51
|
+
// Show graph button for Thread, WiFi, and Ethernet (Ethernet devices are shown in WiFi graph)
|
|
52
|
+
const showGraphButton = networkType === "thread" || networkType === "wifi" || networkType === "ethernet";
|
|
53
|
+
// Ethernet devices go to WiFi graph since they're displayed there
|
|
54
|
+
const graphViewType = networkType === "ethernet" ? "wifi" : networkType;
|
|
55
|
+
const graphUrl = showGraphButton ? `#${graphViewType}/${this.node.node_id}` : null;
|
|
56
|
+
|
|
49
57
|
return html`
|
|
50
58
|
<dashboard-header
|
|
51
59
|
.title=${"Node " + this.node.node_id}
|
|
@@ -55,6 +63,17 @@ class MatterNodeView extends LitElement {
|
|
|
55
63
|
|
|
56
64
|
<!-- node details section -->
|
|
57
65
|
<div class="container">
|
|
66
|
+
<div class="node-title-bar">
|
|
67
|
+
<h2>Node ${this.node.node_id}</h2>
|
|
68
|
+
${showGraphButton
|
|
69
|
+
? html`
|
|
70
|
+
<a href=${graphUrl} class="show-in-graph-button" title="Show in ${graphViewType} graph">
|
|
71
|
+
<ha-svg-icon .path=${mdiGraphOutline}></ha-svg-icon>
|
|
72
|
+
<span class="button-text">Show in graph</span>
|
|
73
|
+
</a>
|
|
74
|
+
`
|
|
75
|
+
: ""}
|
|
76
|
+
</div>
|
|
58
77
|
<node-details .node=${this.node} .client=${this.client}></node-details>
|
|
59
78
|
</div>
|
|
60
79
|
|
|
@@ -122,5 +141,60 @@ class MatterNodeView extends LitElement {
|
|
|
122
141
|
font-weight: bold;
|
|
123
142
|
font-size: 0.8em;
|
|
124
143
|
}
|
|
144
|
+
|
|
145
|
+
.node-title-bar {
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
gap: 16px;
|
|
149
|
+
margin-bottom: 16px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.node-title-bar h2 {
|
|
153
|
+
margin: 0;
|
|
154
|
+
font-size: 1.25rem;
|
|
155
|
+
font-weight: 500;
|
|
156
|
+
color: var(--md-sys-color-on-background, #333);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.show-in-graph-button {
|
|
160
|
+
display: inline-flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
gap: 6px;
|
|
163
|
+
padding: 6px 12px;
|
|
164
|
+
background-color: var(--md-sys-color-primary);
|
|
165
|
+
color: var(--md-sys-color-on-primary);
|
|
166
|
+
text-decoration: none;
|
|
167
|
+
border-radius: 4px;
|
|
168
|
+
font-size: 0.8rem;
|
|
169
|
+
font-weight: 500;
|
|
170
|
+
transition: opacity 0.2s;
|
|
171
|
+
white-space: nowrap;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.show-in-graph-button:hover {
|
|
175
|
+
opacity: 0.9;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.show-in-graph-button ha-svg-icon {
|
|
179
|
+
--icon-primary-color: var(--md-sys-color-on-primary);
|
|
180
|
+
width: 16px;
|
|
181
|
+
height: 16px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@media (max-width: 768px) {
|
|
185
|
+
.show-in-graph-button {
|
|
186
|
+
display: none;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@media (max-width: 480px) {
|
|
191
|
+
.show-in-graph-button .button-text {
|
|
192
|
+
display: none;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.show-in-graph-button {
|
|
196
|
+
padding: 6px;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
125
199
|
`;
|
|
126
200
|
}
|
|
@@ -16,6 +16,7 @@ import "../components/ha-svg-icon";
|
|
|
16
16
|
import { formatNodeAddress } from "../util/format_hex.js";
|
|
17
17
|
import "./components/footer";
|
|
18
18
|
import "./components/header";
|
|
19
|
+
import type { ActiveView } from "./components/header.js";
|
|
19
20
|
import "./components/server-details";
|
|
20
21
|
|
|
21
22
|
declare global {
|
|
@@ -31,6 +32,15 @@ class MatterServerView extends LitElement {
|
|
|
31
32
|
@property()
|
|
32
33
|
public nodes!: MatterClient["nodes"];
|
|
33
34
|
|
|
35
|
+
@property()
|
|
36
|
+
public activeView?: ActiveView;
|
|
37
|
+
|
|
38
|
+
@property({ type: Boolean })
|
|
39
|
+
public hasThreadDevices?: boolean;
|
|
40
|
+
|
|
41
|
+
@property({ type: Boolean })
|
|
42
|
+
public hasWifiDevices?: boolean;
|
|
43
|
+
|
|
34
44
|
private _cachedNodes?: MatterClient["nodes"];
|
|
35
45
|
private _cachedNodeEntries?: [string, MatterNode][];
|
|
36
46
|
|
|
@@ -46,7 +56,13 @@ class MatterServerView extends LitElement {
|
|
|
46
56
|
const nodes = this.getNodeEntries(this.nodes);
|
|
47
57
|
|
|
48
58
|
return html`
|
|
49
|
-
<dashboard-header
|
|
59
|
+
<dashboard-header
|
|
60
|
+
title="Open Home Foundation Matter Server"
|
|
61
|
+
.client=${this.client}
|
|
62
|
+
.activeView=${this.activeView}
|
|
63
|
+
.hasThreadDevices=${this.hasThreadDevices}
|
|
64
|
+
.hasWifiDevices=${this.hasWifiDevices}
|
|
65
|
+
></dashboard-header>
|
|
50
66
|
|
|
51
67
|
<!-- server details section -->
|
|
52
68
|
<div class="container">
|