@matter-server/dashboard 1.0.0 → 1.1.0
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/components/dialogs/acl/acl-actions.d.ts +12 -0
- package/dist/esm/components/dialogs/acl/acl-actions.d.ts.map +1 -0
- package/dist/esm/components/dialogs/acl/acl-actions.js +56 -0
- package/dist/esm/components/dialogs/acl/acl-actions.js.map +6 -0
- package/dist/esm/components/dialogs/acl/model.d.ts +2 -2
- package/dist/esm/components/dialogs/acl/model.d.ts.map +1 -1
- package/dist/esm/components/dialogs/acl/model.js +11 -15
- package/dist/esm/components/dialogs/acl/model.js.map +1 -1
- package/dist/esm/components/dialogs/acl/node-acl-add-dialog.d.ts +43 -0
- package/dist/esm/components/dialogs/acl/node-acl-add-dialog.d.ts.map +1 -0
- package/dist/esm/components/dialogs/acl/node-acl-add-dialog.js +353 -0
- package/dist/esm/components/dialogs/acl/node-acl-add-dialog.js.map +6 -0
- package/dist/esm/components/dialogs/acl/show-node-acl-add-dialog.d.ts +8 -0
- package/dist/esm/components/dialogs/acl/show-node-acl-add-dialog.d.ts.map +1 -0
- package/dist/esm/components/dialogs/acl/show-node-acl-add-dialog.js +15 -0
- package/dist/esm/components/dialogs/acl/show-node-acl-add-dialog.js.map +6 -0
- package/dist/esm/components/dialogs/binding/binding-actions.d.ts +17 -0
- package/dist/esm/components/dialogs/binding/binding-actions.d.ts.map +1 -0
- package/dist/esm/components/dialogs/binding/binding-actions.js +135 -0
- package/dist/esm/components/dialogs/binding/binding-actions.js.map +6 -0
- package/dist/esm/components/dialogs/binding/model.d.ts +1 -1
- package/dist/esm/components/dialogs/binding/model.d.ts.map +1 -1
- package/dist/esm/components/dialogs/binding/model.js +8 -3
- package/dist/esm/components/dialogs/binding/model.js.map +1 -1
- package/dist/esm/components/dialogs/binding/node-binding-dialog.d.ts +16 -24
- package/dist/esm/components/dialogs/binding/node-binding-dialog.d.ts.map +1 -1
- package/dist/esm/components/dialogs/binding/node-binding-dialog.js +210 -332
- package/dist/esm/components/dialogs/binding/node-binding-dialog.js.map +1 -1
- package/dist/esm/pages/cluster-commands/clusters/access-control-commands.d.ts +37 -0
- package/dist/esm/pages/cluster-commands/clusters/access-control-commands.d.ts.map +1 -0
- package/dist/esm/pages/cluster-commands/clusters/access-control-commands.js +387 -0
- package/dist/esm/pages/cluster-commands/clusters/access-control-commands.js.map +6 -0
- package/dist/esm/pages/cluster-commands/clusters/binding-commands.d.ts +35 -0
- package/dist/esm/pages/cluster-commands/clusters/binding-commands.d.ts.map +1 -0
- package/dist/esm/pages/cluster-commands/clusters/binding-commands.js +254 -0
- package/dist/esm/pages/cluster-commands/clusters/binding-commands.js.map +6 -0
- package/dist/esm/pages/cluster-commands/index.d.ts +2 -0
- package/dist/esm/pages/cluster-commands/index.d.ts.map +1 -1
- package/dist/esm/pages/cluster-commands/index.js +2 -0
- package/dist/esm/pages/cluster-commands/index.js.map +1 -1
- package/dist/esm/pages/components/node-details.d.ts +0 -1
- package/dist/esm/pages/components/node-details.d.ts.map +1 -1
- package/dist/esm/pages/components/node-details.js +2 -18
- package/dist/esm/pages/components/node-details.js.map +1 -1
- package/dist/esm/pages/components/server-details.js +3 -3
- package/dist/esm/pages/components/server-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 +2 -1
- package/dist/esm/pages/matter-cluster-view.js.map +1 -1
- package/dist/esm/pages/matter-endpoint-view.d.ts +3 -3
- package/dist/esm/pages/matter-endpoint-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-endpoint-view.js +13 -10
- package/dist/esm/pages/matter-endpoint-view.js.map +1 -1
- package/dist/esm/pages/matter-node-view.js +2 -2
- package/dist/esm/pages/matter-node-view.js.map +1 -1
- package/dist/esm/pages/network/network-utils.d.ts +1 -5
- package/dist/esm/pages/network/network-utils.d.ts.map +1 -1
- package/dist/esm/pages/network/network-utils.js +1 -11
- package/dist/esm/pages/network/network-utils.js.map +1 -1
- package/dist/esm/util/access-control.d.ts +54 -0
- package/dist/esm/util/access-control.d.ts.map +1 -0
- package/dist/esm/util/access-control.js +100 -0
- package/dist/esm/util/access-control.js.map +6 -0
- package/dist/esm/util/binding.d.ts +54 -0
- package/dist/esm/util/binding.d.ts.map +1 -0
- package/dist/esm/util/binding.js +113 -0
- package/dist/esm/util/binding.js.map +6 -0
- package/dist/esm/util/endpoints.d.ts +9 -0
- package/dist/esm/util/endpoints.d.ts.map +1 -0
- package/dist/esm/util/endpoints.js +18 -0
- package/dist/esm/util/endpoints.js.map +6 -0
- package/dist/esm/util/node-name.d.ts +12 -0
- package/dist/esm/util/node-name.d.ts.map +1 -0
- package/dist/esm/util/node-name.js +15 -0
- package/dist/esm/util/node-name.js.map +6 -0
- package/dist/web/js/{attribute-write-dialog-W7xpCE2E.js → attribute-write-dialog-CqqdRniU.js} +1 -1
- package/dist/web/js/{command-invoke-dialog-BAqAAdJw.js → command-invoke-dialog-BuvBOrdC.js} +1 -1
- package/dist/web/js/{commission-node-dialog-BTzCGgdy.js → commission-node-dialog-nVZp3go0.js} +5 -5
- package/dist/web/js/{commission-node-existing-B2M2hyDh.js → commission-node-existing-Cx3Ahk2t.js} +2 -2
- package/dist/web/js/{commission-node-thread-djdz2dXW.js → commission-node-thread-CI8mWWPs.js} +2 -2
- package/dist/web/js/{commission-node-wifi-DxAYNS1A.js → commission-node-wifi-lUX4LK2R.js} +2 -2
- package/dist/web/js/{dialog-box-tHvPVxDN.js → dialog-box-bAdbnf-T.js} +1 -1
- package/dist/web/js/{fire_event-BleYfTLc.js → fire_event-tWhqPfdz.js} +1 -1
- package/dist/web/js/main.js +1 -1
- package/dist/web/js/{matter-dashboard-app-CVi_GDky.js → matter-dashboard-app-CONA_608.js} +1485 -390
- package/dist/web/js/node-acl-add-dialog-DlR-sF-b.js +320 -0
- package/dist/web/js/node-binding-dialog-DhnX_86M.js +267 -0
- package/dist/web/js/{node-label-dialog-DVZSjsXU.js → node-label-dialog-T3nPG-Qy.js} +1 -1
- package/dist/web/js/{settings-dialog-BG5MgZcO.js → settings-dialog-DrHzJtsi.js} +1 -1
- package/package.json +4 -4
- package/src/components/dialogs/acl/acl-actions.ts +71 -0
- package/src/components/dialogs/acl/model.ts +18 -17
- package/src/components/dialogs/acl/node-acl-add-dialog.ts +350 -0
- package/src/components/dialogs/acl/show-node-acl-add-dialog.ts +14 -0
- package/src/components/dialogs/binding/binding-actions.ts +201 -0
- package/src/components/dialogs/binding/model.ts +11 -4
- package/src/components/dialogs/binding/node-binding-dialog.ts +221 -399
- package/src/pages/cluster-commands/clusters/access-control-commands.ts +407 -0
- package/src/pages/cluster-commands/clusters/binding-commands.ts +273 -0
- package/src/pages/cluster-commands/index.ts +2 -0
- package/src/pages/components/node-details.ts +2 -21
- package/src/pages/components/server-details.ts +3 -3
- package/src/pages/matter-cluster-view.ts +4 -1
- package/src/pages/matter-endpoint-view.ts +16 -10
- package/src/pages/matter-node-view.ts +2 -2
- package/src/pages/network/network-utils.ts +1 -18
- package/src/util/access-control.ts +135 -0
- package/src/util/binding.ts +182 -0
- package/src/util/endpoints.ts +17 -0
- package/src/util/node-name.ts +18 -0
- package/dist/web/js/node-binding-dialog-B9IdqHrZ.js +0 -624
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { i, c, a as clientContext, n, r, b as i$1, P as Privilege, p as nodeIdKey, q as aclCapacity, s as showAlertDialog, u as targetServerClusters, v as clusters, x as AuthMode, y as addAclEntry, z as PRIVILEGE_NAMES, d as b, B as mdiClose, C as getDeviceName, h as handleAsync, g as t } from './matter-dashboard-app-CONA_608.js';
|
|
2
|
+
import { p as preventDefault } from './prevent_default-D-ohDGsN.js';
|
|
3
|
+
import './main.js';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
8
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
9
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
10
|
+
if (kind && result) __defProp(target, key, result);
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
13
|
+
let NodeAclAddDialog = class extends i$1 {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this._privilege = Privilege.Operate;
|
|
17
|
+
this._subjects = new Array();
|
|
18
|
+
this._subjectInput = "";
|
|
19
|
+
this._targets = new Array();
|
|
20
|
+
this._targetEndpoint = "all";
|
|
21
|
+
this._targetCluster = "";
|
|
22
|
+
this._busy = false;
|
|
23
|
+
}
|
|
24
|
+
_knownNodes() {
|
|
25
|
+
return Object.values(this.client.nodes).sort((a, b) => {
|
|
26
|
+
const x = BigInt(a.node_id);
|
|
27
|
+
const y = BigInt(b.node_id);
|
|
28
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
_addSubject(raw) {
|
|
32
|
+
const value = raw.trim();
|
|
33
|
+
if (!/^\d+$/.test(value)) return;
|
|
34
|
+
const id = BigInt(value);
|
|
35
|
+
const key = nodeIdKey(id);
|
|
36
|
+
if (this._subjects.some(s => nodeIdKey(s) === key)) return;
|
|
37
|
+
const max = aclCapacity(this.node).subjectsMax;
|
|
38
|
+
if (max > 0 && this._subjects.length >= max) {
|
|
39
|
+
void showAlertDialog({
|
|
40
|
+
title: "Limit reached",
|
|
41
|
+
text: `At most ${max} subjects per entry.`
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this._subjects = [...this._subjects, id];
|
|
46
|
+
this._subjectInput = "";
|
|
47
|
+
}
|
|
48
|
+
_removeSubject(key) {
|
|
49
|
+
this._subjects = this._subjects.filter(s => nodeIdKey(s) !== key);
|
|
50
|
+
}
|
|
51
|
+
_nodeEndpoints() {
|
|
52
|
+
const eps = /* @__PURE__ */new Set();
|
|
53
|
+
for (const key of Object.keys(this.node.attributes)) {
|
|
54
|
+
const m = /^(\d+)\/29\/0$/.exec(key);
|
|
55
|
+
if (m) eps.add(Number(m[1]));
|
|
56
|
+
}
|
|
57
|
+
return Array.from(eps).sort((a, b) => a - b);
|
|
58
|
+
}
|
|
59
|
+
_clusterOptions() {
|
|
60
|
+
if (this._targetEndpoint === "all") {
|
|
61
|
+
const all = /* @__PURE__ */new Set();
|
|
62
|
+
for (const ep of this._nodeEndpoints()) targetServerClusters(this.node, ep).forEach(c => all.add(c));
|
|
63
|
+
return Array.from(all).sort((a, b) => a - b);
|
|
64
|
+
}
|
|
65
|
+
return targetServerClusters(this.node, Number(this._targetEndpoint)).sort((a, b) => a - b);
|
|
66
|
+
}
|
|
67
|
+
_clusterLabel(id) {
|
|
68
|
+
var _clusters$id;
|
|
69
|
+
return `${((_clusters$id = clusters[id]) === null || _clusters$id === void 0 ? void 0 : _clusters$id.label) ?? "Cluster"} (0x${id.toString(16).padStart(2, "0").toUpperCase()})`;
|
|
70
|
+
}
|
|
71
|
+
_addTarget() {
|
|
72
|
+
const max = aclCapacity(this.node).targetsMax;
|
|
73
|
+
if (max > 0 && this._targets.length >= max) {
|
|
74
|
+
void showAlertDialog({
|
|
75
|
+
title: "Limit reached",
|
|
76
|
+
text: `At most ${max} targets per entry.`
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const endpoint = this._targetEndpoint === "all" || this._targetEndpoint === "" ? void 0 : Number(this._targetEndpoint);
|
|
81
|
+
const cluster = this._targetCluster === "all" || this._targetCluster === "" ? void 0 : Number(this._targetCluster);
|
|
82
|
+
if (endpoint === void 0 && cluster === void 0) {
|
|
83
|
+
void showAlertDialog({
|
|
84
|
+
title: "Validation error",
|
|
85
|
+
text: "Pick an endpoint and/or a cluster for the target."
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this._targets = [...this._targets, {
|
|
90
|
+
endpoint,
|
|
91
|
+
cluster,
|
|
92
|
+
deviceType: void 0
|
|
93
|
+
}];
|
|
94
|
+
this._targetEndpoint = "all";
|
|
95
|
+
this._targetCluster = "all";
|
|
96
|
+
}
|
|
97
|
+
_removeTarget(index) {
|
|
98
|
+
this._targets = this._targets.filter((_, i) => i !== index);
|
|
99
|
+
}
|
|
100
|
+
async _save() {
|
|
101
|
+
if (this._subjects.length === 0) {
|
|
102
|
+
await showAlertDialog({
|
|
103
|
+
title: "Validation error",
|
|
104
|
+
text: "Add at least one subject node."
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const entry = {
|
|
109
|
+
privilege: this._privilege,
|
|
110
|
+
authMode: AuthMode.Case,
|
|
111
|
+
subjects: this._subjects,
|
|
112
|
+
targets: this._targets.length ? this._targets : void 0,
|
|
113
|
+
fabricIndex: 0
|
|
114
|
+
};
|
|
115
|
+
this._busy = true;
|
|
116
|
+
try {
|
|
117
|
+
await addAclEntry(this.client, this.node.node_id, entry);
|
|
118
|
+
this._close();
|
|
119
|
+
} catch (err) {
|
|
120
|
+
await showAlertDialog({
|
|
121
|
+
title: "Failed to add entry",
|
|
122
|
+
text: err instanceof Error ? err.message : String(err)
|
|
123
|
+
});
|
|
124
|
+
} finally {
|
|
125
|
+
this._busy = false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
_close() {
|
|
129
|
+
this.shadowRoot.querySelector("md-dialog").close();
|
|
130
|
+
}
|
|
131
|
+
_handleClosed() {
|
|
132
|
+
var _this$parentNode;
|
|
133
|
+
(_this$parentNode = this.parentNode) === null || _this$parentNode === void 0 || _this$parentNode.removeChild(this);
|
|
134
|
+
}
|
|
135
|
+
render() {
|
|
136
|
+
return b`
|
|
137
|
+
<md-dialog open @cancel=${preventDefault} @closed=${this._handleClosed}>
|
|
138
|
+
<div slot="headline">Add ACL entry</div>
|
|
139
|
+
<div slot="content">
|
|
140
|
+
<div class="form">
|
|
141
|
+
<md-outlined-select
|
|
142
|
+
label="Privilege"
|
|
143
|
+
.value=${String(this._privilege)}
|
|
144
|
+
?disabled=${this._busy}
|
|
145
|
+
@change=${e => this._privilege = Number(e.target.value)}
|
|
146
|
+
>
|
|
147
|
+
${[Privilege.View, Privilege.Operate, Privilege.Manage, Privilege.Administer].map(p => b`<md-select-option value=${String(p)}
|
|
148
|
+
><div slot="headline">${PRIVILEGE_NAMES[p]} · ${p}</div></md-select-option
|
|
149
|
+
>`)}
|
|
150
|
+
</md-outlined-select>
|
|
151
|
+
<div class="note">Auth mode: CASE (node). Group subjects are not supported yet.</div>
|
|
152
|
+
|
|
153
|
+
<div class="label">Subjects (nodes)</div>
|
|
154
|
+
<div class="chips">
|
|
155
|
+
${this._subjects.length === 0 ? b`<span class="mut">none — add at least one</span>` : this._subjects.map(s => {
|
|
156
|
+
const known = this.client.nodes[nodeIdKey(s)];
|
|
157
|
+
return b`<span class="chip"
|
|
158
|
+
>${known ? getDeviceName(known) : "Node"} · ${s.toString()}
|
|
159
|
+
<ha-svg-icon
|
|
160
|
+
class="x"
|
|
161
|
+
.path=${mdiClose}
|
|
162
|
+
@click=${() => this._removeSubject(nodeIdKey(s))}
|
|
163
|
+
></ha-svg-icon
|
|
164
|
+
></span>`;
|
|
165
|
+
})}
|
|
166
|
+
</div>
|
|
167
|
+
<div class="row">
|
|
168
|
+
<md-outlined-select
|
|
169
|
+
label="Known nodes"
|
|
170
|
+
?disabled=${this._busy}
|
|
171
|
+
@change=${e => {
|
|
172
|
+
const v = e.target.value;
|
|
173
|
+
if (v) this._addSubject(v);
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
<md-select-option value=""><div slot="headline">— pick —</div></md-select-option>
|
|
177
|
+
${this._knownNodes().map(n => b`<md-select-option value=${nodeIdKey(n.node_id)}
|
|
178
|
+
><div slot="headline">
|
|
179
|
+
${n.node_id.toString()} · ${getDeviceName(n)}
|
|
180
|
+
</div></md-select-option
|
|
181
|
+
>`)}
|
|
182
|
+
</md-outlined-select>
|
|
183
|
+
<md-outlined-text-field
|
|
184
|
+
label="or raw node id"
|
|
185
|
+
type="text"
|
|
186
|
+
pattern="[0-9]+"
|
|
187
|
+
.value=${this._subjectInput}
|
|
188
|
+
?disabled=${this._busy}
|
|
189
|
+
@input=${e => this._subjectInput = e.target.value}
|
|
190
|
+
></md-outlined-text-field>
|
|
191
|
+
<md-text-button ?disabled=${this._busy} @click=${() => this._addSubject(this._subjectInput)}
|
|
192
|
+
>Add</md-text-button
|
|
193
|
+
>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="label">Targets (optional — none means whole node)</div>
|
|
197
|
+
<div class="chips">
|
|
198
|
+
${this._targets.length === 0 ? b`<span class="mut">whole node</span>` : this._targets.map((t, i) => b`<span class="chip"
|
|
199
|
+
>${t.endpoint != null ? `EP ${t.endpoint}` : "All endpoints"}
|
|
200
|
+
${t.cluster != null ? `\xB7 ${this._clusterLabel(t.cluster)}` : "\xB7 all clusters"}
|
|
201
|
+
<ha-svg-icon
|
|
202
|
+
class="x"
|
|
203
|
+
.path=${mdiClose}
|
|
204
|
+
@click=${() => this._removeTarget(i)}
|
|
205
|
+
></ha-svg-icon
|
|
206
|
+
></span>`)}
|
|
207
|
+
</div>
|
|
208
|
+
<div class="row">
|
|
209
|
+
<md-outlined-select
|
|
210
|
+
label="endpoint"
|
|
211
|
+
.value=${this._targetEndpoint}
|
|
212
|
+
?disabled=${this._busy}
|
|
213
|
+
@change=${e => {
|
|
214
|
+
this._targetEndpoint = e.target.value;
|
|
215
|
+
this._targetCluster = "";
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
<md-select-option value="all"
|
|
219
|
+
><div slot="headline">All endpoints</div></md-select-option
|
|
220
|
+
>
|
|
221
|
+
${this._nodeEndpoints().map(ep => b`<md-select-option value=${String(ep)}
|
|
222
|
+
><div slot="headline">EP ${ep}</div></md-select-option
|
|
223
|
+
>`)}
|
|
224
|
+
</md-outlined-select>
|
|
225
|
+
<md-outlined-select
|
|
226
|
+
label="cluster"
|
|
227
|
+
.value=${this._targetCluster}
|
|
228
|
+
?disabled=${this._busy}
|
|
229
|
+
@change=${e => this._targetCluster = e.target.value}
|
|
230
|
+
>
|
|
231
|
+
<md-select-option value="all"><div slot="headline">All clusters</div></md-select-option>
|
|
232
|
+
${this._clusterOptions().map(c => b`<md-select-option value=${String(c)}
|
|
233
|
+
><div slot="headline">${this._clusterLabel(c)}</div></md-select-option
|
|
234
|
+
>`)}
|
|
235
|
+
</md-outlined-select>
|
|
236
|
+
<md-text-button ?disabled=${this._busy} @click=${() => this._addTarget()}
|
|
237
|
+
>Add target</md-text-button
|
|
238
|
+
>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
<div slot="actions">
|
|
243
|
+
<md-text-button ?disabled=${this._busy} @click=${handleAsync(() => this._save())}
|
|
244
|
+
>Add</md-text-button
|
|
245
|
+
>
|
|
246
|
+
<md-text-button ?disabled=${this._busy} @click=${this._close}>Cancel</md-text-button>
|
|
247
|
+
</div>
|
|
248
|
+
</md-dialog>
|
|
249
|
+
`;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
NodeAclAddDialog.styles = i`
|
|
253
|
+
.form {
|
|
254
|
+
display: flex;
|
|
255
|
+
flex-direction: column;
|
|
256
|
+
gap: 10px;
|
|
257
|
+
min-width: 360px;
|
|
258
|
+
}
|
|
259
|
+
.label {
|
|
260
|
+
font-size: 11px;
|
|
261
|
+
text-transform: uppercase;
|
|
262
|
+
letter-spacing: 0.04em;
|
|
263
|
+
opacity: 0.65;
|
|
264
|
+
margin-top: 6px;
|
|
265
|
+
}
|
|
266
|
+
.note {
|
|
267
|
+
font-size: 12px;
|
|
268
|
+
opacity: 0.7;
|
|
269
|
+
}
|
|
270
|
+
.row {
|
|
271
|
+
display: flex;
|
|
272
|
+
gap: 8px;
|
|
273
|
+
align-items: center;
|
|
274
|
+
flex-wrap: wrap;
|
|
275
|
+
}
|
|
276
|
+
.chips {
|
|
277
|
+
display: flex;
|
|
278
|
+
flex-wrap: wrap;
|
|
279
|
+
gap: 6px;
|
|
280
|
+
}
|
|
281
|
+
.chip {
|
|
282
|
+
display: inline-flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
gap: 4px;
|
|
285
|
+
padding: 3px 8px;
|
|
286
|
+
border-radius: 6px;
|
|
287
|
+
font-size: 12px;
|
|
288
|
+
background: var(--md-sys-color-surface-container-high);
|
|
289
|
+
color: var(--md-sys-color-on-surface);
|
|
290
|
+
}
|
|
291
|
+
.chip .x {
|
|
292
|
+
cursor: pointer;
|
|
293
|
+
--mdc-icon-size: 16px;
|
|
294
|
+
width: 16px;
|
|
295
|
+
height: 16px;
|
|
296
|
+
}
|
|
297
|
+
.mut {
|
|
298
|
+
opacity: 0.6;
|
|
299
|
+
font-size: 12px;
|
|
300
|
+
}
|
|
301
|
+
`;
|
|
302
|
+
__decorateClass([c({
|
|
303
|
+
context: clientContext,
|
|
304
|
+
subscribe: true
|
|
305
|
+
}), n({
|
|
306
|
+
attribute: false
|
|
307
|
+
})], NodeAclAddDialog.prototype, "client", 2);
|
|
308
|
+
__decorateClass([n({
|
|
309
|
+
attribute: false
|
|
310
|
+
})], NodeAclAddDialog.prototype, "node", 2);
|
|
311
|
+
__decorateClass([r()], NodeAclAddDialog.prototype, "_privilege", 2);
|
|
312
|
+
__decorateClass([r()], NodeAclAddDialog.prototype, "_subjects", 2);
|
|
313
|
+
__decorateClass([r()], NodeAclAddDialog.prototype, "_subjectInput", 2);
|
|
314
|
+
__decorateClass([r()], NodeAclAddDialog.prototype, "_targets", 2);
|
|
315
|
+
__decorateClass([r()], NodeAclAddDialog.prototype, "_targetEndpoint", 2);
|
|
316
|
+
__decorateClass([r()], NodeAclAddDialog.prototype, "_targetCluster", 2);
|
|
317
|
+
__decorateClass([r()], NodeAclAddDialog.prototype, "_busy", 2);
|
|
318
|
+
NodeAclAddDialog = __decorateClass([t("node-acl-add-dialog")], NodeAclAddDialog);
|
|
319
|
+
|
|
320
|
+
export { NodeAclAddDialog };
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { i, c, a as clientContext, n, r, b as i$1, p as nodeIdKey, v as clusters, s as showAlertDialog, E as targetAclCapacityForBinding, F as addBinding, G as bindableClusters, A, d as b, C as getDeviceName, H as getEndpointDeviceTypes, h as handleAsync, g as t } from './matter-dashboard-app-CONA_608.js';
|
|
2
|
+
import { p as preventDefault } from './prevent_default-D-ohDGsN.js';
|
|
3
|
+
import './main.js';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
8
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
9
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
10
|
+
if (kind && result) __defProp(target, key, result);
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
13
|
+
const ALL_CLUSTERS = "all";
|
|
14
|
+
const CUSTOM_CLUSTER = "custom";
|
|
15
|
+
let NodeBindingDialog = class extends i$1 {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this._nodeIdInput = "";
|
|
19
|
+
this._endpointInput = "";
|
|
20
|
+
this._clusterSelection = ALL_CLUSTERS;
|
|
21
|
+
this._customClusterInput = "";
|
|
22
|
+
this._busy = false;
|
|
23
|
+
}
|
|
24
|
+
_knownNodes() {
|
|
25
|
+
return Object.values(this.client.nodes).filter(n => nodeIdKey(n.node_id) !== nodeIdKey(this.node.node_id)).sort((a, b) => {
|
|
26
|
+
const x = BigInt(a.node_id);
|
|
27
|
+
const y = BigInt(b.node_id);
|
|
28
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
_resolveTarget() {
|
|
32
|
+
const raw = this._nodeIdInput.trim();
|
|
33
|
+
if (!/^\d+$/.test(raw)) return void 0;
|
|
34
|
+
return this.client.nodes[nodeIdKey(BigInt(raw))];
|
|
35
|
+
}
|
|
36
|
+
_nodeEndpoints(target) {
|
|
37
|
+
const eps = /* @__PURE__ */new Set();
|
|
38
|
+
for (const key of Object.keys(target.attributes)) {
|
|
39
|
+
const m = /^(\d+)\/29\/0$/.exec(key);
|
|
40
|
+
if (m) eps.add(Number(m[1]));
|
|
41
|
+
}
|
|
42
|
+
return Array.from(eps).sort((a, b) => a - b);
|
|
43
|
+
}
|
|
44
|
+
_clusterLabel(id) {
|
|
45
|
+
var _clusters$id;
|
|
46
|
+
return `${((_clusters$id = clusters[id]) === null || _clusters$id === void 0 ? void 0 : _clusters$id.label) ?? "Cluster"} (0x${id.toString(16).padStart(2, "0").toUpperCase()})`;
|
|
47
|
+
}
|
|
48
|
+
_onNodeSelect(e) {
|
|
49
|
+
const select = e.target;
|
|
50
|
+
this._nodeIdInput = select.value;
|
|
51
|
+
this._endpointInput = "";
|
|
52
|
+
this._clusterSelection = ALL_CLUSTERS;
|
|
53
|
+
}
|
|
54
|
+
async _add() {
|
|
55
|
+
const target = this._resolveTarget();
|
|
56
|
+
const rawNodeId = this._nodeIdInput.trim();
|
|
57
|
+
if (!/^\d+$/.test(rawNodeId) || BigInt(rawNodeId) <= 0n) {
|
|
58
|
+
await showAlertDialog({
|
|
59
|
+
title: "Validation error",
|
|
60
|
+
text: "Please enter a valid target node id."
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const targetNodeId = BigInt(rawNodeId);
|
|
65
|
+
const endpoint = parseInt(this._endpointInput, 10);
|
|
66
|
+
if (Number.isNaN(endpoint) || endpoint < 0 || endpoint > 65534) {
|
|
67
|
+
await showAlertDialog({
|
|
68
|
+
title: "Validation error",
|
|
69
|
+
text: "Please enter a valid target endpoint."
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let cluster;
|
|
74
|
+
if (this._clusterSelection === ALL_CLUSTERS) {
|
|
75
|
+
cluster = void 0;
|
|
76
|
+
} else if (this._clusterSelection === CUSTOM_CLUSTER) {
|
|
77
|
+
const c = parseInt(this._customClusterInput, 10);
|
|
78
|
+
if (Number.isNaN(c) || c < 0 || c > 32767) {
|
|
79
|
+
await showAlertDialog({
|
|
80
|
+
title: "Validation error",
|
|
81
|
+
text: "Please enter a valid cluster id."
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
cluster = c;
|
|
86
|
+
} else {
|
|
87
|
+
cluster = parseInt(this._clusterSelection, 10);
|
|
88
|
+
}
|
|
89
|
+
if (target) {
|
|
90
|
+
const capacity = targetAclCapacityForBinding(target, this.node.node_id);
|
|
91
|
+
if (!capacity.canAdd) {
|
|
92
|
+
await showAlertDialog({
|
|
93
|
+
title: "Cannot add binding",
|
|
94
|
+
text: capacity.reason ?? "Target ACL is full."
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
this._busy = true;
|
|
100
|
+
try {
|
|
101
|
+
await addBinding(this.client, this.node, this.endpoint, targetNodeId, endpoint, cluster);
|
|
102
|
+
this._close();
|
|
103
|
+
} catch (err) {
|
|
104
|
+
await showAlertDialog({
|
|
105
|
+
title: "Failed to add binding",
|
|
106
|
+
text: err instanceof Error ? err.message : String(err)
|
|
107
|
+
});
|
|
108
|
+
} finally {
|
|
109
|
+
this._busy = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
_close() {
|
|
113
|
+
this.shadowRoot.querySelector("md-dialog").close();
|
|
114
|
+
}
|
|
115
|
+
_handleClosed() {
|
|
116
|
+
var _this$parentNode;
|
|
117
|
+
(_this$parentNode = this.parentNode) === null || _this$parentNode === void 0 || _this$parentNode.removeChild(this);
|
|
118
|
+
}
|
|
119
|
+
_renderClusterField(target, endpoint) {
|
|
120
|
+
const known = target !== void 0 && endpoint !== void 0 && !Number.isNaN(endpoint);
|
|
121
|
+
const split = known ? bindableClusters(this.node, this.endpoint, target, endpoint) : void 0;
|
|
122
|
+
const nonBindable = split !== void 0 && this._clusterSelection !== ALL_CLUSTERS && this._clusterSelection !== CUSTOM_CLUSTER && split.otherTarget.includes(parseInt(this._clusterSelection, 10));
|
|
123
|
+
return b`
|
|
124
|
+
<md-outlined-select
|
|
125
|
+
label="Cluster"
|
|
126
|
+
.value=${this._clusterSelection}
|
|
127
|
+
?disabled=${this._busy}
|
|
128
|
+
@change=${e => this._clusterSelection = e.target.value}
|
|
129
|
+
>
|
|
130
|
+
<md-select-option value=${ALL_CLUSTERS}>
|
|
131
|
+
<div slot="headline">All clusters (any eligible)</div>
|
|
132
|
+
</md-select-option>
|
|
133
|
+
${split && split.bindable.length ? b`<md-select-option disabled><div slot="headline">— Bindable —</div></md-select-option>
|
|
134
|
+
${split.bindable.map(c => b`<md-select-option value=${String(c)}
|
|
135
|
+
><div slot="headline">${this._clusterLabel(c)}</div></md-select-option
|
|
136
|
+
>`)}` : A}
|
|
137
|
+
${split && split.otherTarget.length ? b`<md-select-option disabled
|
|
138
|
+
><div slot="headline">— Other target clusters (⚠) —</div></md-select-option
|
|
139
|
+
>
|
|
140
|
+
${split.otherTarget.map(c => b`<md-select-option value=${String(c)}
|
|
141
|
+
><div slot="headline">${this._clusterLabel(c)}</div></md-select-option
|
|
142
|
+
>`)}` : A}
|
|
143
|
+
<md-select-option value=${CUSTOM_CLUSTER}
|
|
144
|
+
><div slot="headline">Custom cluster id…</div></md-select-option
|
|
145
|
+
>
|
|
146
|
+
</md-outlined-select>
|
|
147
|
+
${this._clusterSelection === CUSTOM_CLUSTER ? b`<md-outlined-text-field
|
|
148
|
+
label="cluster id"
|
|
149
|
+
type="number"
|
|
150
|
+
min="0"
|
|
151
|
+
max="32767"
|
|
152
|
+
.value=${this._customClusterInput}
|
|
153
|
+
?disabled=${this._busy}
|
|
154
|
+
@input=${e => this._customClusterInput = e.target.value}
|
|
155
|
+
></md-outlined-text-field>` : A}
|
|
156
|
+
${nonBindable ? b`<div class="warn">
|
|
157
|
+
⚠ This cluster is not a client cluster on the source endpoint. The binding may not function — it
|
|
158
|
+
will be added anyway on your request.
|
|
159
|
+
</div>` : A}
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
render() {
|
|
163
|
+
if (!this.node) return A;
|
|
164
|
+
const target = this._resolveTarget();
|
|
165
|
+
const endpoint = this._endpointInput === "" ? void 0 : parseInt(this._endpointInput, 10);
|
|
166
|
+
const endpoints = target ? this._nodeEndpoints(target) : [];
|
|
167
|
+
return b`
|
|
168
|
+
<md-dialog open @cancel=${preventDefault} @closed=${this._handleClosed}>
|
|
169
|
+
<div slot="headline">Add binding</div>
|
|
170
|
+
<div slot="content">
|
|
171
|
+
<div class="form">
|
|
172
|
+
<md-outlined-select
|
|
173
|
+
label="Known nodes"
|
|
174
|
+
?disabled=${this._busy}
|
|
175
|
+
.value=${target ? this._nodeIdInput : ""}
|
|
176
|
+
@change=${this._onNodeSelect}
|
|
177
|
+
>
|
|
178
|
+
<md-select-option value=""><div slot="headline">— pick a node —</div></md-select-option>
|
|
179
|
+
${this._knownNodes().map(n => b`<md-select-option value=${nodeIdKey(n.node_id)}>
|
|
180
|
+
<div slot="headline">${n.node_id.toString()} · ${getDeviceName(n)}</div>
|
|
181
|
+
</md-select-option>`)}
|
|
182
|
+
</md-outlined-select>
|
|
183
|
+
<md-outlined-text-field
|
|
184
|
+
label="Target node id"
|
|
185
|
+
type="text"
|
|
186
|
+
pattern="[0-9]+"
|
|
187
|
+
supporting-text="required — pick above or enter a raw node id"
|
|
188
|
+
.value=${this._nodeIdInput}
|
|
189
|
+
?disabled=${this._busy}
|
|
190
|
+
@input=${e => {
|
|
191
|
+
this._nodeIdInput = e.target.value;
|
|
192
|
+
this._endpointInput = "";
|
|
193
|
+
this._clusterSelection = ALL_CLUSTERS;
|
|
194
|
+
}}
|
|
195
|
+
></md-outlined-text-field>
|
|
196
|
+
|
|
197
|
+
${target ? b`<md-outlined-select
|
|
198
|
+
label="Target endpoint"
|
|
199
|
+
?disabled=${this._busy}
|
|
200
|
+
.value=${this._endpointInput}
|
|
201
|
+
@change=${e => {
|
|
202
|
+
this._endpointInput = e.target.value;
|
|
203
|
+
this._clusterSelection = ALL_CLUSTERS;
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
${endpoints.map(ep => {
|
|
207
|
+
const dt = getEndpointDeviceTypes(target, ep)[0];
|
|
208
|
+
return b`<md-select-option value=${String(ep)}>
|
|
209
|
+
<div slot="headline">EP ${ep}${dt ? ` \xB7 ${dt.label}` : ""}</div>
|
|
210
|
+
</md-select-option>`;
|
|
211
|
+
})}
|
|
212
|
+
</md-outlined-select>` : b`<md-outlined-text-field
|
|
213
|
+
label="Target endpoint"
|
|
214
|
+
type="number"
|
|
215
|
+
min="0"
|
|
216
|
+
max="65534"
|
|
217
|
+
supporting-text=${this._nodeIdInput.trim() === "" ? "enter a node id first" : "unknown node \u2014 enter endpoint manually"}
|
|
218
|
+
?disabled=${this._busy || this._nodeIdInput.trim() === ""}
|
|
219
|
+
.value=${this._endpointInput}
|
|
220
|
+
@input=${e => this._endpointInput = e.target.value}
|
|
221
|
+
></md-outlined-text-field>`}
|
|
222
|
+
${this._renderClusterField(target, endpoint)}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
<div slot="actions">
|
|
226
|
+
<md-text-button ?disabled=${this._busy} @click=${handleAsync(() => this._add())}
|
|
227
|
+
>Add</md-text-button
|
|
228
|
+
>
|
|
229
|
+
<md-text-button ?disabled=${this._busy} @click=${this._close}>Cancel</md-text-button>
|
|
230
|
+
</div>
|
|
231
|
+
</md-dialog>
|
|
232
|
+
`;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
NodeBindingDialog.styles = i`
|
|
236
|
+
.form {
|
|
237
|
+
display: flex;
|
|
238
|
+
flex-direction: column;
|
|
239
|
+
gap: 12px;
|
|
240
|
+
min-width: 320px;
|
|
241
|
+
}
|
|
242
|
+
.warn {
|
|
243
|
+
font-size: 12px;
|
|
244
|
+
padding: 8px 10px;
|
|
245
|
+
border-radius: 7px;
|
|
246
|
+
background: var(--md-sys-color-error-container);
|
|
247
|
+
color: var(--md-sys-color-on-error-container);
|
|
248
|
+
}
|
|
249
|
+
`;
|
|
250
|
+
__decorateClass([c({
|
|
251
|
+
context: clientContext,
|
|
252
|
+
subscribe: true
|
|
253
|
+
}), n({
|
|
254
|
+
attribute: false
|
|
255
|
+
})], NodeBindingDialog.prototype, "client", 2);
|
|
256
|
+
__decorateClass([n()], NodeBindingDialog.prototype, "node", 2);
|
|
257
|
+
__decorateClass([n({
|
|
258
|
+
attribute: false
|
|
259
|
+
})], NodeBindingDialog.prototype, "endpoint", 2);
|
|
260
|
+
__decorateClass([r()], NodeBindingDialog.prototype, "_nodeIdInput", 2);
|
|
261
|
+
__decorateClass([r()], NodeBindingDialog.prototype, "_endpointInput", 2);
|
|
262
|
+
__decorateClass([r()], NodeBindingDialog.prototype, "_clusterSelection", 2);
|
|
263
|
+
__decorateClass([r()], NodeBindingDialog.prototype, "_customClusterInput", 2);
|
|
264
|
+
__decorateClass([r()], NodeBindingDialog.prototype, "_busy", 2);
|
|
265
|
+
NodeBindingDialog = __decorateClass([t("node-binding-dialog")], NodeBindingDialog);
|
|
266
|
+
|
|
267
|
+
export { NodeBindingDialog };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r, n, g as t, b as i, M as MAX_NODE_LABEL_LENGTH, d as b, w as writeNodeLabel, s as showAlertDialog } from './matter-dashboard-app-
|
|
1
|
+
import { r, n, g as t, b as i, M as MAX_NODE_LABEL_LENGTH, d as b, w as writeNodeLabel, s as showAlertDialog } from './matter-dashboard-app-CONA_608.js';
|
|
2
2
|
import { p as preventDefault } from './prevent_default-D-ohDGsN.js';
|
|
3
3
|
import './main.js';
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i, c, a as clientContext, t as tickContext, r, e, b as i$1, f as fireAndForget, s as showAlertDialog, d as b, A, h as handleAsync, g as t, n, D as DevModeService, m as mdiWifi, j as mdiAccessPoint, k as mdiEyeOff, l as mdiEye } from './matter-dashboard-app-
|
|
1
|
+
import { i, c, a as clientContext, t as tickContext, r, e, b as i$1, f as fireAndForget, s as showAlertDialog, d as b, A, h as handleAsync, g as t, n, D as DevModeService, m as mdiWifi, j as mdiAccessPoint, k as mdiEyeOff, l as mdiEye } from './matter-dashboard-app-CONA_608.js';
|
|
2
2
|
import { p as preventDefault } from './prevent_default-D-ohDGsN.js';
|
|
3
3
|
import './main.js';
|
|
4
4
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matter-server/dashboard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Dashboard for OHF Matter Server",
|
|
5
5
|
"homepage": "https://github.com/matter-js/matterjs-server",
|
|
6
6
|
"bugs": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@lit/context": "^1.1.6",
|
|
35
35
|
"@material/web": "^2.4.1",
|
|
36
|
-
"@matter-server/custom-clusters": "1.
|
|
37
|
-
"@matter-server/ws-client": "1.
|
|
36
|
+
"@matter-server/custom-clusters": "1.1.0",
|
|
37
|
+
"@matter-server/ws-client": "1.1.0",
|
|
38
38
|
"@mdi/js": "^7.4.47",
|
|
39
39
|
"lit": "^3.3.3",
|
|
40
40
|
"tslib": "^2.8.1",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@babel/preset-env": "^7.29.7",
|
|
45
|
-
"@matter/main": "0.17.
|
|
45
|
+
"@matter/main": "0.17.3",
|
|
46
46
|
"@rollup/plugin-babel": "^7.1.0",
|
|
47
47
|
"@rollup/plugin-commonjs": "^29.0.3",
|
|
48
48
|
"@rollup/plugin-json": "^6.1.0",
|