@matter-server/dashboard 0.2.7-alpha.0-20260118-993a1c7 → 0.2.7-alpha.0-20260119-49e7237
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/binding/node-binding-dialog.d.ts.map +1 -1
- package/dist/esm/components/dialogs/binding/node-binding-dialog.js +3 -2
- package/dist/esm/components/dialogs/binding/node-binding-dialog.js.map +1 -1
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.d.ts.map +1 -1
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.js +2 -1
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.js.map +1 -1
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.d.ts.map +1 -1
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.js +3 -2
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.js.map +1 -1
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.d.ts.map +1 -1
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.js +5 -4
- package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.js.map +1 -1
- package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts +33 -0
- package/dist/esm/components/dialogs/settings/log-level-dialog.d.ts.map +1 -0
- package/dist/esm/components/dialogs/settings/log-level-dialog.js +189 -0
- package/dist/esm/components/dialogs/settings/log-level-dialog.js.map +6 -0
- package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts +8 -0
- package/dist/esm/components/dialogs/settings/show-log-level-dialog.d.ts.map +1 -0
- package/dist/esm/components/dialogs/settings/show-log-level-dialog.js +15 -0
- package/dist/esm/components/dialogs/settings/show-log-level-dialog.js.map +6 -0
- package/dist/esm/pages/cluster-commands/clusters/level-control-commands.d.ts.map +1 -1
- package/dist/esm/pages/cluster-commands/clusters/level-control-commands.js +5 -2
- package/dist/esm/pages/cluster-commands/clusters/level-control-commands.js.map +1 -1
- package/dist/esm/pages/cluster-commands/clusters/on-off-commands.d.ts.map +1 -1
- package/dist/esm/pages/cluster-commands/clusters/on-off-commands.js +6 -3
- package/dist/esm/pages/cluster-commands/clusters/on-off-commands.js.map +1 -1
- package/dist/esm/pages/components/header.d.ts +1 -0
- package/dist/esm/pages/components/header.d.ts.map +1 -1
- package/dist/esm/pages/components/header.js +13 -1
- package/dist/esm/pages/components/header.js.map +1 -1
- package/dist/esm/pages/components/node-details.d.ts.map +1 -1
- package/dist/esm/pages/components/node-details.js +6 -5
- 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 +3 -2
- package/dist/esm/pages/components/server-details.js.map +1 -1
- package/dist/esm/pages/matter-server-view.d.ts.map +1 -1
- package/dist/esm/pages/matter-server-view.js +14 -1
- package/dist/esm/pages/matter-server-view.js.map +1 -1
- package/dist/esm/util/async-handler.d.ts +50 -0
- package/dist/esm/util/async-handler.d.ts.map +1 -0
- package/dist/esm/util/async-handler.js +33 -0
- package/dist/esm/util/async-handler.js.map +6 -0
- package/dist/esm/util/fire_event.d.ts.map +1 -1
- package/dist/esm/util/fire_event.js +1 -2
- package/dist/esm/util/fire_event.js.map +1 -1
- package/dist/esm/util/format_hex.d.ts +13 -2
- package/dist/esm/util/format_hex.d.ts.map +1 -1
- package/dist/esm/util/format_hex.js +6 -1
- package/dist/esm/util/format_hex.js.map +1 -1
- package/dist/web/js/{commission-node-dialog-DGw5qDgH.js → commission-node-dialog-B4_wgFye.js} +5 -5
- package/dist/web/js/{commission-node-existing-CHyyeC8y.js → commission-node-existing-BktL7vHX.js} +6 -5
- package/dist/web/js/{commission-node-thread-iRDSlidy.js → commission-node-thread-ox6fB3dO.js} +7 -6
- package/dist/web/js/{commission-node-wifi-C4YNR3bG.js → commission-node-wifi-Dlayi41Z.js} +9 -8
- package/dist/web/js/{dialog-box-ag-xOaYh.js → dialog-box-DFs0hcPv.js} +2 -2
- package/dist/web/js/{fire_event-BeiEbHcE.js → fire_event-oVtV3_P5.js} +2 -3
- package/dist/web/js/log-level-dialog-BVxKJJ49.js +3235 -0
- package/dist/web/js/main.js +170 -69
- package/dist/web/js/{matter-dashboard-app-BxQ4W_uT.js → matter-dashboard-app-Dr-IYMsD.js} +138 -17
- package/dist/web/js/{node-binding-dialog-ClziphM0.js → node-binding-dialog-CqE3VuZN.js} +6 -5
- package/dist/web/js/outlined-text-field-BVcHUxiS.js +968 -0
- package/dist/web/js/{prevent_default-Bs2sUnny.js → prevent_default-tLhqZmsK.js} +1 -1
- package/dist/web/js/validator-muF28wYx.js +1122 -0
- package/package.json +4 -4
- package/src/components/dialogs/binding/node-binding-dialog.ts +3 -2
- package/src/components/dialogs/commission-node-dialog/commission-node-existing.ts +2 -1
- package/src/components/dialogs/commission-node-dialog/commission-node-thread.ts +3 -2
- package/src/components/dialogs/commission-node-dialog/commission-node-wifi.ts +5 -4
- package/src/components/dialogs/settings/log-level-dialog.ts +183 -0
- package/src/components/dialogs/settings/show-log-level-dialog.ts +14 -0
- package/src/pages/cluster-commands/clusters/level-control-commands.ts +5 -2
- package/src/pages/cluster-commands/clusters/on-off-commands.ts +6 -3
- package/src/pages/components/header.ts +16 -1
- package/src/pages/components/node-details.ts +6 -5
- package/src/pages/components/server-details.ts +3 -3
- package/src/pages/matter-server-view.ts +17 -2
- package/src/util/async-handler.ts +74 -0
- package/src/util/fire_event.ts +1 -3
- package/src/util/format_hex.ts +17 -2
- package/dist/web/js/outlined-text-field-B-CiqgEJ.js +0 -2086
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matter-server/dashboard",
|
|
3
|
-
"version": "0.2.7-alpha.0-
|
|
3
|
+
"version": "0.2.7-alpha.0-20260119-49e7237",
|
|
4
4
|
"description": "Dashboard for OHF Matter Server",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/matter-js/matterjs-server/issues"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@babel/preset-env": "^7.28.5",
|
|
26
|
-
"@matter/main": "0.16.
|
|
26
|
+
"@matter/main": "0.16.6-alpha.0-20260118-ceec17415",
|
|
27
27
|
"@rollup/plugin-babel": "^6.1.0",
|
|
28
28
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
29
29
|
"rollup-plugin-copy": "^3.5.0",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@lit/context": "^1.1.6",
|
|
39
39
|
"@material/web": "^2.4.1",
|
|
40
|
-
"@matter-server/ws-client": "0.2.7-alpha.0-
|
|
41
|
-
"@matter-server/custom-clusters": "0.2.7-alpha.0-
|
|
40
|
+
"@matter-server/ws-client": "0.2.7-alpha.0-20260119-49e7237",
|
|
41
|
+
"@matter-server/custom-clusters": "0.2.7-alpha.0-20260119-49e7237",
|
|
42
42
|
"@mdi/js": "^7.4.47",
|
|
43
43
|
"lit": "^3.3.1",
|
|
44
44
|
"tslib": "^2.8.1"
|
|
@@ -16,6 +16,7 @@ import "../../../components/ha-svg-icon";
|
|
|
16
16
|
import { AccessControlEntry, BindingTarget, MatterClient, MatterNode } from "@matter-server/ws-client";
|
|
17
17
|
import { css, html, LitElement, nothing } from "lit";
|
|
18
18
|
import { customElement, property, query } from "lit/decorators.js";
|
|
19
|
+
import { handleAsync } from "../../../util/async-handler.js";
|
|
19
20
|
import { preventDefault } from "../../../util/prevent_default.js";
|
|
20
21
|
import { BindingEntryDataTransformer, BindingEntryStruct, InputType } from "./model.js";
|
|
21
22
|
|
|
@@ -314,7 +315,7 @@ export class NodeBindingDialog extends LitElement {
|
|
|
314
315
|
</div>
|
|
315
316
|
<div slot="end">
|
|
316
317
|
<md-text-button
|
|
317
|
-
@click=${() => this.deleteBindingHandler(index)}
|
|
318
|
+
@click=${handleAsync(() => this.deleteBindingHandler(index))}
|
|
318
319
|
>delete</md-text-button
|
|
319
320
|
</div>
|
|
320
321
|
</md-list-item>
|
|
@@ -368,7 +369,7 @@ export class NodeBindingDialog extends LitElement {
|
|
|
368
369
|
</div>
|
|
369
370
|
</div>
|
|
370
371
|
<div slot="actions">
|
|
371
|
-
<md-text-button @click=${this.addBindingHandler}>Add</md-text-button>
|
|
372
|
+
<md-text-button @click=${handleAsync(() => this.addBindingHandler())}>Add</md-text-button>
|
|
372
373
|
<md-text-button @click=${this._close}>Cancel</md-text-button>
|
|
373
374
|
</div>
|
|
374
375
|
</md-dialog>
|
|
@@ -12,6 +12,7 @@ import { MatterClient } from "@matter-server/ws-client";
|
|
|
12
12
|
import { LitElement, html, nothing } from "lit";
|
|
13
13
|
import { customElement, property, query, state } from "lit/decorators.js";
|
|
14
14
|
import { clientContext } from "../../../client/client-context.js";
|
|
15
|
+
import { handleAsync } from "../../../util/async-handler.js";
|
|
15
16
|
import { fireEvent } from "../../../util/fire_event.js";
|
|
16
17
|
|
|
17
18
|
@customElement("commission-node-existing")
|
|
@@ -30,7 +31,7 @@ export class CommissionNodeExisting extends LitElement {
|
|
|
30
31
|
return html`<md-outlined-text-field label="Share code" .disabled="${this._loading}"> </md-outlined-text-field>
|
|
31
32
|
<br />
|
|
32
33
|
<br />
|
|
33
|
-
<md-outlined-button @click=${this._commissionNode} .disabled="${this._loading}"
|
|
34
|
+
<md-outlined-button @click=${handleAsync(() => this._commissionNode())} .disabled="${this._loading}"
|
|
34
35
|
>Commission</md-outlined-button
|
|
35
36
|
>${this._loading ? html`<md-circular-progress indeterminate></md-circular-progress>` : nothing}`;
|
|
36
37
|
}
|
|
@@ -12,6 +12,7 @@ import { MatterClient } from "@matter-server/ws-client";
|
|
|
12
12
|
import { LitElement, html, nothing } from "lit";
|
|
13
13
|
import { customElement, property, query, state } from "lit/decorators.js";
|
|
14
14
|
import { clientContext } from "../../../client/client-context.js";
|
|
15
|
+
import { handleAsync } from "../../../util/async-handler.js";
|
|
15
16
|
import { fireEvent } from "../../../util/fire_event.js";
|
|
16
17
|
|
|
17
18
|
@customElement("commission-node-thread")
|
|
@@ -34,14 +35,14 @@ export class CommissionNodeThread extends LitElement {
|
|
|
34
35
|
</md-outlined-text-field>
|
|
35
36
|
<br />
|
|
36
37
|
<br />
|
|
37
|
-
<md-outlined-button @click=${this._setThreadDataset} .disabled="${this._loading}"
|
|
38
|
+
<md-outlined-button @click=${handleAsync(() => this._setThreadDataset())} .disabled="${this._loading}"
|
|
38
39
|
>Set Thread Dataset</md-outlined-button
|
|
39
40
|
>${this._loading ? html`<md-circular-progress indeterminate></md-circular-progress>` : nothing}`;
|
|
40
41
|
}
|
|
41
42
|
return html`<md-outlined-text-field label="Pairing code" .disabled="${this._loading}"> </md-outlined-text-field>
|
|
42
43
|
<br />
|
|
43
44
|
<br />
|
|
44
|
-
<md-outlined-button @click=${this._commissionNode} .disabled="${this._loading}"
|
|
45
|
+
<md-outlined-button @click=${handleAsync(() => this._commissionNode())} .disabled="${this._loading}"
|
|
45
46
|
>Commission</md-outlined-button
|
|
46
47
|
>${this._loading ? html`<md-circular-progress indeterminate></md-circular-progress>` : nothing}`;
|
|
47
48
|
}
|
|
@@ -12,6 +12,7 @@ import { MatterClient } from "@matter-server/ws-client";
|
|
|
12
12
|
import { LitElement, html, nothing } from "lit";
|
|
13
13
|
import { customElement, property, query, state } from "lit/decorators.js";
|
|
14
14
|
import { clientContext } from "../../../client/client-context.js";
|
|
15
|
+
import { handleAsync } from "../../../util/async-handler.js";
|
|
15
16
|
import { fireEvent } from "../../../util/fire_event.js";
|
|
16
17
|
|
|
17
18
|
@customElement("commission-node-wifi")
|
|
@@ -37,7 +38,7 @@ export class CommissionNodeWifi extends LitElement {
|
|
|
37
38
|
</md-outlined-text-field>
|
|
38
39
|
<br />
|
|
39
40
|
<br />
|
|
40
|
-
<md-outlined-button @click=${this._setWifiCredentials} .disabled="${this._loading}"
|
|
41
|
+
<md-outlined-button @click=${handleAsync(() => this._setWifiCredentials())} .disabled="${this._loading}"
|
|
41
42
|
>Set WiFi Credentials</md-outlined-button
|
|
42
43
|
>${this._loading
|
|
43
44
|
? html`<md-circular-progress indeterminate .visible="${this._loading}"></md-circular-progress>`
|
|
@@ -46,12 +47,12 @@ export class CommissionNodeWifi extends LitElement {
|
|
|
46
47
|
return html`<md-outlined-text-field label="Pairing code" .disabled="${this._loading}"> </md-outlined-text-field>
|
|
47
48
|
<br />
|
|
48
49
|
<br />
|
|
49
|
-
<md-outlined-button @click=${this._commissionNode} .disabled="${this._loading}"
|
|
50
|
+
<md-outlined-button @click=${handleAsync(() => this._commissionNode())} .disabled="${this._loading}"
|
|
50
51
|
>Commission</md-outlined-button
|
|
51
52
|
>${this._loading ? html`<md-circular-progress indeterminate></md-circular-progress>` : nothing}`;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
private _setWifiCredentials() {
|
|
55
|
+
private async _setWifiCredentials() {
|
|
55
56
|
const ssid = this._ssidField.value;
|
|
56
57
|
if (!ssid) {
|
|
57
58
|
alert("SSID is required");
|
|
@@ -64,7 +65,7 @@ export class CommissionNodeWifi extends LitElement {
|
|
|
64
65
|
}
|
|
65
66
|
this._loading = true;
|
|
66
67
|
try {
|
|
67
|
-
this.client.setWifiCredentials(ssid, password);
|
|
68
|
+
await this.client.setWifiCredentials(ssid, password);
|
|
68
69
|
} catch (err) {
|
|
69
70
|
alert(`Error setting WiFi credentials: \n${(err as Error).message}`);
|
|
70
71
|
} finally {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import "@material/web/button/text-button";
|
|
8
|
+
import "@material/web/dialog/dialog";
|
|
9
|
+
import type { MdDialog } from "@material/web/dialog/dialog.js";
|
|
10
|
+
import "@material/web/select/outlined-select";
|
|
11
|
+
import type { MdOutlinedSelect } from "@material/web/select/outlined-select.js";
|
|
12
|
+
import "@material/web/select/select-option";
|
|
13
|
+
import { LogLevelString, MatterClient } from "@matter-server/ws-client";
|
|
14
|
+
import { css, html, LitElement, nothing } from "lit";
|
|
15
|
+
import { customElement, property, query, state } from "lit/decorators.js";
|
|
16
|
+
import { fireAndForget, handleAsync } from "../../../util/async-handler.js";
|
|
17
|
+
import { preventDefault } from "../../../util/prevent_default.js";
|
|
18
|
+
|
|
19
|
+
const LOG_LEVELS: { value: LogLevelString; label: string }[] = [
|
|
20
|
+
{ value: "critical", label: "Critical" },
|
|
21
|
+
{ value: "error", label: "Error" },
|
|
22
|
+
{ value: "warning", label: "Warning" },
|
|
23
|
+
{ value: "info", label: "Info" },
|
|
24
|
+
{ value: "debug", label: "Debug" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
@customElement("log-level-dialog")
|
|
28
|
+
export class LogLevelDialog extends LitElement {
|
|
29
|
+
@property({ attribute: false })
|
|
30
|
+
public client!: MatterClient;
|
|
31
|
+
|
|
32
|
+
@state() private _consoleLevel: LogLevelString = "info";
|
|
33
|
+
@state() private _fileLevel: LogLevelString | null = null;
|
|
34
|
+
@state() private _loading = true;
|
|
35
|
+
@state() private _applying = false;
|
|
36
|
+
|
|
37
|
+
@query("md-outlined-select[name='console']")
|
|
38
|
+
private _consoleSelect!: MdOutlinedSelect;
|
|
39
|
+
|
|
40
|
+
@query("md-outlined-select[name='file']")
|
|
41
|
+
private _fileSelect?: MdOutlinedSelect;
|
|
42
|
+
|
|
43
|
+
override connectedCallback() {
|
|
44
|
+
super.connectedCallback();
|
|
45
|
+
fireAndForget(this._loadLogLevels());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private async _loadLogLevels() {
|
|
49
|
+
try {
|
|
50
|
+
const result = await this.client.getLogLevel();
|
|
51
|
+
this._consoleLevel = result.console_loglevel;
|
|
52
|
+
this._fileLevel = result.file_loglevel;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error("Failed to load log levels:", err);
|
|
55
|
+
} finally {
|
|
56
|
+
this._loading = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private async _apply() {
|
|
61
|
+
this._applying = true;
|
|
62
|
+
try {
|
|
63
|
+
const consoleLevel = this._consoleSelect.value as LogLevelString;
|
|
64
|
+
const fileLevel = this._fileSelect?.value as LogLevelString | undefined;
|
|
65
|
+
|
|
66
|
+
const result = await this.client.setLogLevel(
|
|
67
|
+
consoleLevel,
|
|
68
|
+
this._fileLevel !== null ? fileLevel : undefined,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
this._consoleLevel = result.console_loglevel;
|
|
72
|
+
this._fileLevel = result.file_loglevel;
|
|
73
|
+
this._close();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error("Failed to apply log levels:", err);
|
|
76
|
+
alert("Failed to apply log levels");
|
|
77
|
+
} finally {
|
|
78
|
+
this._applying = false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private _close() {
|
|
83
|
+
this.shadowRoot!.querySelector<MdDialog>("md-dialog")!.close();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private _handleClosed() {
|
|
87
|
+
this.parentNode!.removeChild(this);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected override render() {
|
|
91
|
+
return html`
|
|
92
|
+
<md-dialog open @cancel=${preventDefault} @closed=${this._handleClosed}>
|
|
93
|
+
<div slot="headline">Server Log Settings</div>
|
|
94
|
+
<div slot="content">
|
|
95
|
+
${this._loading
|
|
96
|
+
? html`<p class="loading">Loading...</p>`
|
|
97
|
+
: html`
|
|
98
|
+
<p class="hint">Changes are temporary and will be reset on the next server restart.</p>
|
|
99
|
+
<div class="form-field">
|
|
100
|
+
<label>Console Log Level</label>
|
|
101
|
+
<md-outlined-select name="console" .value=${this._consoleLevel}>
|
|
102
|
+
${LOG_LEVELS.map(
|
|
103
|
+
level => html`
|
|
104
|
+
<md-select-option
|
|
105
|
+
value=${level.value}
|
|
106
|
+
?selected=${level.value === this._consoleLevel}
|
|
107
|
+
>
|
|
108
|
+
<div slot="headline">${level.label}</div>
|
|
109
|
+
</md-select-option>
|
|
110
|
+
`,
|
|
111
|
+
)}
|
|
112
|
+
</md-outlined-select>
|
|
113
|
+
</div>
|
|
114
|
+
${this._fileLevel !== null
|
|
115
|
+
? html`
|
|
116
|
+
<div class="form-field">
|
|
117
|
+
<label>File Log Level</label>
|
|
118
|
+
<md-outlined-select name="file" .value=${this._fileLevel}>
|
|
119
|
+
${LOG_LEVELS.map(
|
|
120
|
+
level => html`
|
|
121
|
+
<md-select-option
|
|
122
|
+
value=${level.value}
|
|
123
|
+
?selected=${level.value === this._fileLevel}
|
|
124
|
+
>
|
|
125
|
+
<div slot="headline">${level.label}</div>
|
|
126
|
+
</md-select-option>
|
|
127
|
+
`,
|
|
128
|
+
)}
|
|
129
|
+
</md-outlined-select>
|
|
130
|
+
</div>
|
|
131
|
+
`
|
|
132
|
+
: nothing}
|
|
133
|
+
`}
|
|
134
|
+
</div>
|
|
135
|
+
<div slot="actions">
|
|
136
|
+
<md-text-button @click=${this._close}>Cancel</md-text-button>
|
|
137
|
+
<md-text-button
|
|
138
|
+
@click=${handleAsync(() => this._apply())}
|
|
139
|
+
?disabled=${this._loading || this._applying}
|
|
140
|
+
>
|
|
141
|
+
${this._applying ? "Applying..." : "Apply"}
|
|
142
|
+
</md-text-button>
|
|
143
|
+
</div>
|
|
144
|
+
</md-dialog>
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static override styles = css`
|
|
149
|
+
.loading {
|
|
150
|
+
text-align: center;
|
|
151
|
+
padding: 24px;
|
|
152
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.hint {
|
|
156
|
+
font-size: 0.875rem;
|
|
157
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
158
|
+
margin: 0 0 16px 0;
|
|
159
|
+
font-style: italic;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.form-field {
|
|
163
|
+
margin-bottom: 16px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.form-field label {
|
|
167
|
+
display: block;
|
|
168
|
+
margin-bottom: 8px;
|
|
169
|
+
font-weight: 500;
|
|
170
|
+
color: var(--md-sys-color-on-surface);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
md-outlined-select {
|
|
174
|
+
width: 100%;
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
declare global {
|
|
180
|
+
interface HTMLElementTagNameMap {
|
|
181
|
+
"log-level-dialog": LogLevelDialog;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { MatterClient } from "@matter-server/ws-client";
|
|
8
|
+
|
|
9
|
+
export const showLogLevelDialog = async (client: MatterClient) => {
|
|
10
|
+
await import("./log-level-dialog.js");
|
|
11
|
+
const dialog = document.createElement("log-level-dialog");
|
|
12
|
+
dialog.client = client;
|
|
13
|
+
document.querySelector("matter-dashboard-app")?.renderRoot.appendChild(dialog);
|
|
14
|
+
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import "@material/web/button/filled-button";
|
|
8
8
|
import { html } from "lit";
|
|
9
9
|
import { customElement, state } from "lit/decorators.js";
|
|
10
|
+
import { handleAsync } from "../../../util/async-handler.js";
|
|
10
11
|
import { BaseClusterCommands } from "../base-cluster-commands.js";
|
|
11
12
|
import { registerClusterCommands } from "../registry.js";
|
|
12
13
|
|
|
@@ -60,8 +61,10 @@ class LevelControlClusterCommands extends BaseClusterCommands {
|
|
|
60
61
|
/>
|
|
61
62
|
Execute if Off
|
|
62
63
|
</label>
|
|
63
|
-
<md-outlined-button @click=${this._handleMoveToLevel}
|
|
64
|
-
|
|
64
|
+
<md-outlined-button @click=${handleAsync(() => this._handleMoveToLevel())}
|
|
65
|
+
>MoveToLevel</md-outlined-button
|
|
66
|
+
>
|
|
67
|
+
<md-outlined-button @click=${handleAsync(() => this._handleMoveToLevelWithOnOff())}
|
|
65
68
|
>MoveToLevelWithOnOff</md-outlined-button
|
|
66
69
|
>
|
|
67
70
|
</div>
|
|
@@ -8,6 +8,7 @@ import "@material/web/button/filled-button";
|
|
|
8
8
|
import "@material/web/button/outlined-button";
|
|
9
9
|
import { html } from "lit";
|
|
10
10
|
import { customElement } from "lit/decorators.js";
|
|
11
|
+
import { handleAsync } from "../../../util/async-handler.js";
|
|
11
12
|
import { BaseClusterCommands } from "../base-cluster-commands.js";
|
|
12
13
|
import { registerClusterCommands } from "../registry.js";
|
|
13
14
|
|
|
@@ -25,9 +26,11 @@ class OnOffClusterCommands extends BaseClusterCommands {
|
|
|
25
26
|
<summary>OnOff Commands</summary>
|
|
26
27
|
<div class="command-content">
|
|
27
28
|
<div class="command-row">
|
|
28
|
-
<md-outlined-button @click=${this._handleOn}>On</md-outlined-button>
|
|
29
|
-
<md-outlined-button @click=${this._handleOff}>Off</md-outlined-button>
|
|
30
|
-
<md-outlined-button @click=${this._handleToggle}
|
|
29
|
+
<md-outlined-button @click=${handleAsync(() => this._handleOn())}>On</md-outlined-button>
|
|
30
|
+
<md-outlined-button @click=${handleAsync(() => this._handleOff())}>Off</md-outlined-button>
|
|
31
|
+
<md-outlined-button @click=${handleAsync(() => this._handleToggle())}
|
|
32
|
+
>Toggle</md-outlined-button
|
|
33
|
+
>
|
|
31
34
|
</div>
|
|
32
35
|
</div>
|
|
33
36
|
</details>
|
|
@@ -10,9 +10,10 @@ import "@material/web/iconbutton/icon-button";
|
|
|
10
10
|
import "@material/web/list/list";
|
|
11
11
|
import "@material/web/list/list-item";
|
|
12
12
|
import { MatterClient } from "@matter-server/ws-client";
|
|
13
|
-
import { mdiArrowLeft, mdiBrightnessAuto, mdiLogout, mdiWeatherNight, mdiWeatherSunny } from "@mdi/js";
|
|
13
|
+
import { mdiArrowLeft, mdiBrightnessAuto, mdiCog, mdiLogout, mdiWeatherNight, mdiWeatherSunny } from "@mdi/js";
|
|
14
14
|
import { LitElement, css, html, nothing } from "lit";
|
|
15
15
|
import { customElement, property, state } from "lit/decorators.js";
|
|
16
|
+
import { showLogLevelDialog } from "../../components/dialogs/settings/show-log-level-dialog.js";
|
|
16
17
|
import "../../components/ha-svg-icon";
|
|
17
18
|
import { EffectiveTheme, ThemePreference, ThemeService } from "../../util/theme-service.js";
|
|
18
19
|
|
|
@@ -51,6 +52,12 @@ export class DashboardHeader extends LitElement {
|
|
|
51
52
|
ThemeService.cycleTheme();
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
private _openSettings() {
|
|
56
|
+
if (this.client) {
|
|
57
|
+
showLogLevelDialog(this.client);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
private _getThemeIcon(): string {
|
|
55
62
|
switch (this._themePreference) {
|
|
56
63
|
case "light":
|
|
@@ -95,6 +102,14 @@ export class DashboardHeader extends LitElement {
|
|
|
95
102
|
</md-icon-button>
|
|
96
103
|
`;
|
|
97
104
|
})}
|
|
105
|
+
<!-- settings button (only when connected) -->
|
|
106
|
+
${this.client
|
|
107
|
+
? html`
|
|
108
|
+
<md-icon-button @click=${this._openSettings} title="Server Settings">
|
|
109
|
+
<ha-svg-icon .path=${mdiCog}></ha-svg-icon>
|
|
110
|
+
</md-icon-button>
|
|
111
|
+
`
|
|
112
|
+
: nothing}
|
|
98
113
|
<!-- theme toggle button -->
|
|
99
114
|
<md-icon-button @click=${this._cycleTheme} .title=${this._getThemeTooltip()}>
|
|
100
115
|
<ha-svg-icon .path=${this._getThemeIcon()}></ha-svg-icon>
|
|
@@ -21,6 +21,7 @@ import { DeviceType } from "../../client/models/descriptions.js";
|
|
|
21
21
|
import { showAlertDialog, showPromptDialog } from "../../components/dialog-box/show-dialog-box.js";
|
|
22
22
|
import { showNodeBindingDialog } from "../../components/dialogs/binding/show-node-binding-dialog.js";
|
|
23
23
|
import "../../components/ha-svg-icon";
|
|
24
|
+
import { handleAsync } from "../../util/async-handler.js";
|
|
24
25
|
import { getEndpointDeviceTypes } from "../matter-endpoint-view.js";
|
|
25
26
|
import { bindingContext } from "./context.js";
|
|
26
27
|
|
|
@@ -104,7 +105,7 @@ export class NodeDetails extends LitElement {
|
|
|
104
105
|
</div>`}
|
|
105
106
|
</md-list-item>
|
|
106
107
|
<md-list-item class="btn">
|
|
107
|
-
<md-outlined-button @click=${this._reinterview}
|
|
108
|
+
<md-outlined-button @click=${handleAsync(() => this._reinterview())}
|
|
108
109
|
>Interview<ha-svg-icon slot="icon" .path=${mdiChatProcessing}></ha-svg-icon
|
|
109
110
|
></md-outlined-button>
|
|
110
111
|
${this._updateInitiated
|
|
@@ -118,22 +119,22 @@ export class NodeDetails extends LitElement {
|
|
|
118
119
|
this.node.updateStateProgress,
|
|
119
120
|
)}<ha-svg-icon slot="icon" .path=${mdiUpdate}></ha-svg-icon
|
|
120
121
|
></md-outlined-button>`
|
|
121
|
-
: html`<md-outlined-button @click=${this._searchUpdate}
|
|
122
|
+
: html`<md-outlined-button @click=${handleAsync(() => this._searchUpdate())}
|
|
122
123
|
>Update<ha-svg-icon slot="icon" .path=${mdiUpdate}></ha-svg-icon
|
|
123
124
|
></md-outlined-button>`}
|
|
124
125
|
${bindings
|
|
125
126
|
? html`
|
|
126
|
-
<md-outlined-button @click=${this._binding}>
|
|
127
|
+
<md-outlined-button @click=${handleAsync(() => this._binding())}>
|
|
127
128
|
Binding
|
|
128
129
|
<ha-svg-icon slot="icon" .path=${mdiLink}></ha-svg-icon>
|
|
129
130
|
</md-outlined-button>
|
|
130
131
|
`
|
|
131
132
|
: nothing}
|
|
132
133
|
|
|
133
|
-
<md-outlined-button @click=${this._openCommissioningWindow}
|
|
134
|
+
<md-outlined-button @click=${handleAsync(() => this._openCommissioningWindow())}
|
|
134
135
|
>Share<ha-svg-icon slot="icon" .path=${mdiShareVariant}></ha-svg-icon
|
|
135
136
|
></md-outlined-button>
|
|
136
|
-
<md-outlined-button @click=${this._remove}
|
|
137
|
+
<md-outlined-button @click=${handleAsync(() => this._remove())}
|
|
137
138
|
>Remove<ha-svg-icon slot="icon" .path=${mdiTrashCan}></ha-svg-icon
|
|
138
139
|
></md-outlined-button>
|
|
139
140
|
</md-list-item>
|
|
@@ -18,6 +18,7 @@ import { customElement } from "lit/decorators.js";
|
|
|
18
18
|
import { showAlertDialog, showPromptDialog } from "../../components/dialog-box/show-dialog-box.js";
|
|
19
19
|
import { showCommissionNodeDialog } from "../../components/dialogs/commission-node-dialog/show-commission-node-dialog.js";
|
|
20
20
|
import "../../components/ha-svg-icon";
|
|
21
|
+
import { handleAsync } from "../../util/async-handler.js";
|
|
21
22
|
|
|
22
23
|
@customElement("server-details")
|
|
23
24
|
export class ServerDetails extends LitElement {
|
|
@@ -54,7 +55,7 @@ export class ServerDetails extends LitElement {
|
|
|
54
55
|
<md-list-item class="btn">
|
|
55
56
|
<span>
|
|
56
57
|
<md-outlined-button @click=${this._commissionNode}>Commission node<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon></md-outlined-button>
|
|
57
|
-
<md-outlined-button @click=${this._uploadDiagnosticsDumpFile}>Import node<ha-svg-icon slot="icon" .path=${mdiFile}></ha-svg-icon></md-outlined-button>
|
|
58
|
+
<md-outlined-button @click=${handleAsync(() => this._uploadDiagnosticsDumpFile())}>Import node<ha-svg-icon slot="icon" .path=${mdiFile}></ha-svg-icon></md-outlined-button>
|
|
58
59
|
</md-list-item>
|
|
59
60
|
</md-list>
|
|
60
61
|
<!-- hidden file element for the upload diagnostics -->
|
|
@@ -83,8 +84,7 @@ export class ServerDetails extends LitElement {
|
|
|
83
84
|
) {
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
const fileElem = this.renderRoot.getElementById("fileElem") as HTMLInputElement;
|
|
87
|
+
const fileElem = this.shadowRoot!.getElementById("fileElem") as HTMLInputElement;
|
|
88
88
|
fileElem.click();
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -8,11 +8,12 @@ import "@material/web/divider/divider";
|
|
|
8
8
|
import "@material/web/iconbutton/icon-button";
|
|
9
9
|
import "@material/web/list/list";
|
|
10
10
|
import "@material/web/list/list-item";
|
|
11
|
-
import { MatterClient, MatterNode } from "@matter-server/ws-client";
|
|
11
|
+
import { isTestNodeId, MatterClient, MatterNode } from "@matter-server/ws-client";
|
|
12
12
|
import { mdiChevronRight } from "@mdi/js";
|
|
13
|
-
import {
|
|
13
|
+
import { css, html, LitElement, nothing } from "lit";
|
|
14
14
|
import { customElement, property } from "lit/decorators.js";
|
|
15
15
|
import "../components/ha-svg-icon";
|
|
16
|
+
import { formatNodeAddress } from "../util/format_hex.js";
|
|
16
17
|
import "./components/footer";
|
|
17
18
|
import "./components/header";
|
|
18
19
|
import "./components/server-details";
|
|
@@ -65,6 +66,15 @@ class MatterServerView extends LitElement {
|
|
|
65
66
|
<md-list-item type="link" href=${`#node/${node.node_id}`}>
|
|
66
67
|
<div slot="headline">
|
|
67
68
|
Node ${node.node_id}
|
|
69
|
+
<span class="hex-id"
|
|
70
|
+
>(${formatNodeAddress(
|
|
71
|
+
isTestNodeId(node.node_id) ||
|
|
72
|
+
this.client.serverInfo.fabric_index === undefined
|
|
73
|
+
? undefined
|
|
74
|
+
: this.client.serverInfo.fabric_index,
|
|
75
|
+
node.node_id,
|
|
76
|
+
)})</span
|
|
77
|
+
>
|
|
68
78
|
${node.available ? "" : html`<span class="status">OFFLINE</span>`}
|
|
69
79
|
</div>
|
|
70
80
|
<div slot="supporting-text">
|
|
@@ -113,5 +123,10 @@ class MatterServerView extends LitElement {
|
|
|
113
123
|
font-weight: bold;
|
|
114
124
|
font-size: 0.8em;
|
|
115
125
|
}
|
|
126
|
+
|
|
127
|
+
.hex-id {
|
|
128
|
+
color: var(--text-color, rgba(0, 0, 0, 0.6));
|
|
129
|
+
font-size: 0.85em;
|
|
130
|
+
}
|
|
116
131
|
`;
|
|
117
132
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wraps an async function to be used as an event handler.
|
|
9
|
+
* Catches any errors and logs them, optionally calling an error callback.
|
|
10
|
+
*
|
|
11
|
+
* Usage in Lit templates:
|
|
12
|
+
* ```
|
|
13
|
+
* <button @click=${handleAsync(() => this._save())}>Save</button>
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* With custom error handling:
|
|
17
|
+
* ```
|
|
18
|
+
* <button @click=${handleAsync(() => this._save(), (e) => this._showError(e))}>Save</button>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @param fn - Async function to execute
|
|
22
|
+
* @param onError - Optional error callback
|
|
23
|
+
* @returns Event handler function
|
|
24
|
+
*/
|
|
25
|
+
export function handleAsync<T>(fn: () => Promise<T>, onError?: (error: Error) => void): () => void {
|
|
26
|
+
return () => {
|
|
27
|
+
fn().catch((error: Error) => {
|
|
28
|
+
console.error("Async operation failed:", error);
|
|
29
|
+
onError?.(error);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Wraps an async function that takes an event parameter.
|
|
36
|
+
*
|
|
37
|
+
* Usage:
|
|
38
|
+
* ```
|
|
39
|
+
* <input @change=${handleAsyncEvent((e) => this._handleChange(e))}>
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function handleAsyncEvent<E extends Event, T>(
|
|
43
|
+
fn: (event: E) => Promise<T>,
|
|
44
|
+
onError?: (error: Error) => void,
|
|
45
|
+
): (event: E) => void {
|
|
46
|
+
return (event: E) => {
|
|
47
|
+
fn(event).catch((error: Error) => {
|
|
48
|
+
console.error("Async operation failed:", error);
|
|
49
|
+
onError?.(error);
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Fire-and-forget helper for calling async methods from sync contexts.
|
|
56
|
+
* Use this when you need to call an async method but can't await it.
|
|
57
|
+
*
|
|
58
|
+
* Usage in lifecycle methods:
|
|
59
|
+
* ```
|
|
60
|
+
* connectedCallback() {
|
|
61
|
+
* super.connectedCallback();
|
|
62
|
+
* fireAndForget(this._loadData());
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @param promise - Promise to execute
|
|
67
|
+
* @param onError - Optional error callback
|
|
68
|
+
*/
|
|
69
|
+
export function fireAndForget<T>(promise: Promise<T>, onError?: (error: Error) => void): void {
|
|
70
|
+
promise.catch((error: Error) => {
|
|
71
|
+
console.error("Async operation failed:", error);
|
|
72
|
+
onError?.(error);
|
|
73
|
+
});
|
|
74
|
+
}
|
package/src/util/fire_event.ts
CHANGED
|
@@ -70,14 +70,12 @@ export const fireEvent = <HassEvent extends ValidHassDomEvent>(
|
|
|
70
70
|
},
|
|
71
71
|
) => {
|
|
72
72
|
options = options || {};
|
|
73
|
-
// @ts-expect-error why?
|
|
74
|
-
detail = detail === null || detail === undefined ? {} : detail;
|
|
75
73
|
const event = new Event(type, {
|
|
76
74
|
bubbles: options.bubbles === undefined ? true : options.bubbles,
|
|
77
75
|
cancelable: Boolean(options.cancelable),
|
|
78
76
|
composed: options.composed === undefined ? true : options.composed,
|
|
79
77
|
});
|
|
80
|
-
(event as any).detail = detail;
|
|
78
|
+
(event as any).detail = detail ?? {};
|
|
81
79
|
node.dispatchEvent(event);
|
|
82
80
|
return event;
|
|
83
81
|
};
|