@matter-server/dashboard 0.2.0-alpha.0-00000000-000000000

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.
Files changed (172) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +11 -0
  3. package/dist/esm/client/client-context.d.ts +10 -0
  4. package/dist/esm/client/client-context.d.ts.map +1 -0
  5. package/dist/esm/client/client-context.js +11 -0
  6. package/dist/esm/client/client-context.js.map +6 -0
  7. package/dist/esm/client/models/descriptions.d.ts +20 -0
  8. package/dist/esm/client/models/descriptions.d.ts.map +1 -0
  9. package/dist/esm/client/models/descriptions.js +10929 -0
  10. package/dist/esm/client/models/descriptions.js.map +6 -0
  11. package/dist/esm/components/dialog-box/dialog-box.d.ts +25 -0
  12. package/dist/esm/components/dialog-box/dialog-box.d.ts.map +1 -0
  13. package/dist/esm/components/dialog-box/dialog-box.js +66 -0
  14. package/dist/esm/components/dialog-box/dialog-box.js.map +6 -0
  15. package/dist/esm/components/dialog-box/show-dialog-box.d.ts +18 -0
  16. package/dist/esm/components/dialog-box/show-dialog-box.d.ts.map +1 -0
  17. package/dist/esm/components/dialog-box/show-dialog-box.js +22 -0
  18. package/dist/esm/components/dialog-box/show-dialog-box.js.map +6 -0
  19. package/dist/esm/components/dialogs/acl/model.d.ts +33 -0
  20. package/dist/esm/components/dialogs/acl/model.d.ts.map +1 -0
  21. package/dist/esm/components/dialogs/acl/model.js +79 -0
  22. package/dist/esm/components/dialogs/acl/model.js.map +6 -0
  23. package/dist/esm/components/dialogs/binding/model.d.ts +20 -0
  24. package/dist/esm/components/dialogs/binding/model.d.ts.map +1 -0
  25. package/dist/esm/components/dialogs/binding/model.js +45 -0
  26. package/dist/esm/components/dialogs/binding/model.js.map +6 -0
  27. package/dist/esm/components/dialogs/binding/node-binding-dialog.d.ts +49 -0
  28. package/dist/esm/components/dialogs/binding/node-binding-dialog.d.ts.map +1 -0
  29. package/dist/esm/components/dialogs/binding/node-binding-dialog.js +357 -0
  30. package/dist/esm/components/dialogs/binding/node-binding-dialog.js.map +6 -0
  31. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.d.ts +8 -0
  32. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.d.ts.map +1 -0
  33. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.js +17 -0
  34. package/dist/esm/components/dialogs/binding/show-node-binding-dialog.js.map +6 -0
  35. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.d.ts +31 -0
  36. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.d.ts.map +1 -0
  37. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.js +94 -0
  38. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-dialog.js.map +6 -0
  39. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.d.ts +17 -0
  40. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.d.ts.map +1 -0
  41. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.js +64 -0
  42. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-existing.js.map +6 -0
  43. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.d.ts +19 -0
  44. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.d.ts.map +1 -0
  45. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.js +91 -0
  46. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-thread.js.map +6 -0
  47. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.d.ts +20 -0
  48. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.d.ts.map +1 -0
  49. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.js +106 -0
  50. package/dist/esm/components/dialogs/commission-node-dialog/commission-node-wifi.js.map +6 -0
  51. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.d.ts +8 -0
  52. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.d.ts.map +1 -0
  53. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.js +15 -0
  54. package/dist/esm/components/dialogs/commission-node-dialog/show-commission-node-dialog.js.map +6 -0
  55. package/dist/esm/components/ha-svg-icon.d.ts +19 -0
  56. package/dist/esm/components/ha-svg-icon.d.ts.map +1 -0
  57. package/dist/esm/components/ha-svg-icon.js +77 -0
  58. package/dist/esm/components/ha-svg-icon.js.map +6 -0
  59. package/dist/esm/entrypoint/main.d.ts +7 -0
  60. package/dist/esm/entrypoint/main.d.ts.map +1 -0
  61. package/dist/esm/entrypoint/main.js +45 -0
  62. package/dist/esm/entrypoint/main.js.map +6 -0
  63. package/dist/esm/package.json +3 -0
  64. package/dist/esm/pages/components/context.d.ts +9 -0
  65. package/dist/esm/pages/components/context.d.ts.map +1 -0
  66. package/dist/esm/pages/components/context.js +11 -0
  67. package/dist/esm/pages/components/context.js.map +6 -0
  68. package/dist/esm/pages/components/footer.d.ts +11 -0
  69. package/dist/esm/pages/components/footer.d.ts.map +1 -0
  70. package/dist/esm/pages/components/footer.js +52 -0
  71. package/dist/esm/pages/components/footer.js.map +6 -0
  72. package/dist/esm/pages/components/header.d.ts +27 -0
  73. package/dist/esm/pages/components/header.d.ts.map +1 -0
  74. package/dist/esm/pages/components/header.js +90 -0
  75. package/dist/esm/pages/components/header.js.map +6 -0
  76. package/dist/esm/pages/components/node-details.d.ts +29 -0
  77. package/dist/esm/pages/components/node-details.d.ts.map +1 -0
  78. package/dist/esm/pages/components/node-details.js +241 -0
  79. package/dist/esm/pages/components/node-details.js.map +6 -0
  80. package/dist/esm/pages/components/server-details.d.ts +24 -0
  81. package/dist/esm/pages/components/server-details.d.ts.map +1 -0
  82. package/dist/esm/pages/components/server-details.js +130 -0
  83. package/dist/esm/pages/components/server-details.js.map +6 -0
  84. package/dist/esm/pages/matter-cluster-view.d.ts +30 -0
  85. package/dist/esm/pages/matter-cluster-view.d.ts.map +1 -0
  86. package/dist/esm/pages/matter-cluster-view.js +154 -0
  87. package/dist/esm/pages/matter-cluster-view.js.map +6 -0
  88. package/dist/esm/pages/matter-dashboard-app.d.ts +27 -0
  89. package/dist/esm/pages/matter-dashboard-app.d.ts.map +1 -0
  90. package/dist/esm/pages/matter-dashboard-app.js +122 -0
  91. package/dist/esm/pages/matter-dashboard-app.js.map +6 -0
  92. package/dist/esm/pages/matter-endpoint-view.d.ts +29 -0
  93. package/dist/esm/pages/matter-endpoint-view.d.ts.map +1 -0
  94. package/dist/esm/pages/matter-endpoint-view.js +149 -0
  95. package/dist/esm/pages/matter-endpoint-view.js.map +6 -0
  96. package/dist/esm/pages/matter-node-view.d.ts +28 -0
  97. package/dist/esm/pages/matter-node-view.d.ts.map +1 -0
  98. package/dist/esm/pages/matter-node-view.js +122 -0
  99. package/dist/esm/pages/matter-node-view.js.map +6 -0
  100. package/dist/esm/pages/matter-server-view.d.ts +31 -0
  101. package/dist/esm/pages/matter-server-view.d.ts.map +1 -0
  102. package/dist/esm/pages/matter-server-view.js +113 -0
  103. package/dist/esm/pages/matter-server-view.js.map +6 -0
  104. package/dist/esm/util/clone_class.d.ts +7 -0
  105. package/dist/esm/util/clone_class.d.ts.map +1 -0
  106. package/dist/esm/util/clone_class.js +10 -0
  107. package/dist/esm/util/clone_class.js.map +6 -0
  108. package/dist/esm/util/fire_event.d.ts +34 -0
  109. package/dist/esm/util/fire_event.d.ts.map +1 -0
  110. package/dist/esm/util/fire_event.js +21 -0
  111. package/dist/esm/util/fire_event.js.map +6 -0
  112. package/dist/esm/util/prevent_default.d.ts +7 -0
  113. package/dist/esm/util/prevent_default.d.ts.map +1 -0
  114. package/dist/esm/util/prevent_default.js +10 -0
  115. package/dist/esm/util/prevent_default.js.map +6 -0
  116. package/dist/esm/util/routing.d.ts +10 -0
  117. package/dist/esm/util/routing.d.ts.map +1 -0
  118. package/dist/esm/util/routing.js +6 -0
  119. package/dist/esm/util/routing.js.map +6 -0
  120. package/dist/web/index.html +40 -0
  121. package/dist/web/js/commission-node-dialog-BJsfA4IV.js +78 -0
  122. package/dist/web/js/commission-node-dialog-DEZ3EqYO.js +78 -0
  123. package/dist/web/js/commission-node-existing-CzRtUgBm.js +50 -0
  124. package/dist/web/js/commission-node-existing-OK1ybPFI.js +50 -0
  125. package/dist/web/js/commission-node-thread-DLmclivF.js +75 -0
  126. package/dist/web/js/commission-node-thread-FcLFz84I.js +75 -0
  127. package/dist/web/js/commission-node-wifi-C8ho-UYb.js +88 -0
  128. package/dist/web/js/commission-node-wifi-C8iGfy7c.js +88 -0
  129. package/dist/web/js/dialog-box-BPz-oO3d.js +52 -0
  130. package/dist/web/js/dialog-box-DN32sjfR.js +52 -0
  131. package/dist/web/js/fire_event-BERTqZpV.js +169 -0
  132. package/dist/web/js/fire_event-BlsbXpOL.js +169 -0
  133. package/dist/web/js/main.js +547 -0
  134. package/dist/web/js/matter-dashboard-app-5UjO1Ik8.js +16068 -0
  135. package/dist/web/js/matter-dashboard-app-BazvuIIi.js +16068 -0
  136. package/dist/web/js/node-binding-dialog-2yitVn0R.js +443 -0
  137. package/dist/web/js/node-binding-dialog-Cw6QEmL3.js +443 -0
  138. package/dist/web/js/outlined-text-field-BMLYwwlc.js +2086 -0
  139. package/dist/web/js/outlined-text-field-Sqd4JHxo.js +2086 -0
  140. package/dist/web/js/prevent_default-BsT53c0u.js +814 -0
  141. package/dist/web/js/prevent_default-D4GG_QeD.js +814 -0
  142. package/package.json +54 -0
  143. package/src/client/client-context.ts +10 -0
  144. package/src/client/models/descriptions.ts +10948 -0
  145. package/src/components/dialog-box/dialog-box.ts +62 -0
  146. package/src/components/dialog-box/show-dialog-box.ts +32 -0
  147. package/src/components/dialogs/acl/model.ts +105 -0
  148. package/src/components/dialogs/binding/model.ts +58 -0
  149. package/src/components/dialogs/binding/node-binding-dialog.ts +419 -0
  150. package/src/components/dialogs/binding/show-node-binding-dialog.ts +16 -0
  151. package/src/components/dialogs/commission-node-dialog/commission-node-dialog.ts +102 -0
  152. package/src/components/dialogs/commission-node-dialog/commission-node-existing.ts +49 -0
  153. package/src/components/dialogs/commission-node-dialog/commission-node-thread.ts +76 -0
  154. package/src/components/dialogs/commission-node-dialog/commission-node-wifi.ts +90 -0
  155. package/src/components/dialogs/commission-node-dialog/show-commission-node-dialog.ts +14 -0
  156. package/src/components/ha-svg-icon.ts +66 -0
  157. package/src/entrypoint/main.ts +60 -0
  158. package/src/pages/components/context.ts +10 -0
  159. package/src/pages/components/footer.ts +39 -0
  160. package/src/pages/components/header.ts +87 -0
  161. package/src/pages/components/node-details.ts +252 -0
  162. package/src/pages/components/server-details.ts +124 -0
  163. package/src/pages/matter-cluster-view.ts +162 -0
  164. package/src/pages/matter-dashboard-app.ts +125 -0
  165. package/src/pages/matter-endpoint-view.ts +152 -0
  166. package/src/pages/matter-node-view.ts +126 -0
  167. package/src/pages/matter-server-view.ts +117 -0
  168. package/src/tsconfig.json +16 -0
  169. package/src/util/clone_class.ts +7 -0
  170. package/src/util/fire_event.ts +83 -0
  171. package/src/util/prevent_default.ts +7 -0
  172. package/src/util/routing.ts +10 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import "@material/web/divider/divider";
8
+ import "@material/web/iconbutton/icon-button";
9
+ import "@material/web/list/list";
10
+ import "@material/web/list/list-item";
11
+ import { MatterClient, MatterNode } from "@matter-server/ws-client";
12
+ import { mdiChevronRight } from "@mdi/js";
13
+ import { LitElement, css, html } from "lit";
14
+ import { customElement, property } from "lit/decorators.js";
15
+ import { guard } from "lit/directives/guard.js";
16
+ import { DeviceType, clusters, device_types } from "../client/models/descriptions.js";
17
+ import "../components/ha-svg-icon";
18
+
19
+ declare global {
20
+ interface HTMLElementTagNameMap {
21
+ "matter-endpoint-view": MatterEndpointView;
22
+ }
23
+ }
24
+
25
+ function getUniqueClusters(node: MatterNode, endpoint: number) {
26
+ return Array.from(
27
+ new Set(
28
+ Object.keys(node.attributes)
29
+ .filter(key => key.startsWith(`${endpoint.toString()}/`))
30
+ .map(key => Number(key.split("/")[1])),
31
+ ),
32
+ ).sort((a, b) => {
33
+ return a - b;
34
+ });
35
+ }
36
+
37
+ export function getEndpointDeviceTypes(node: MatterNode, endpoint: number): DeviceType[] {
38
+ const rawValues = node.attributes[`${endpoint}/29/0`] as Record<string, number>[] | undefined;
39
+ if (!rawValues) return [];
40
+ return rawValues.map(rawValue => {
41
+ const id = rawValue["0"] ?? rawValue["deviceType"];
42
+ return device_types[id] || { id: id ?? -1, label: `Unknown Device Type (${id})`, clusters: [] };
43
+ });
44
+ }
45
+
46
+ @customElement("matter-endpoint-view")
47
+ class MatterEndpointView extends LitElement {
48
+ public client!: MatterClient;
49
+
50
+ @property()
51
+ public node?: MatterNode;
52
+
53
+ @property()
54
+ public endpoint?: number;
55
+
56
+ override render() {
57
+ if (!this.node || this.endpoint == undefined) {
58
+ return html`
59
+ <p>Node or endpoint not found!</p>
60
+ <button @click=${this._goBack}>Back</button>
61
+ `;
62
+ }
63
+
64
+ return html`
65
+ <dashboard-header
66
+ .title=${`Node ${this.node.node_id} | Endpoint ${this.endpoint}`}
67
+ .backButton=${`#node/${this.node.node_id}`}
68
+ .client=${this.client}
69
+ ></dashboard-header>
70
+
71
+ <!-- node details section -->
72
+ <div class="container">
73
+ <node-details .node=${this.node} .client=${this.client}></node-details>
74
+ </div>
75
+
76
+ <!-- Endpoint clusters listing -->
77
+ <div class="container">
78
+ <md-list>
79
+ <md-list-item>
80
+ <div slot="headline">
81
+ <b>Clusters on Endpoint ${this.endpoint}</b>
82
+ </div>
83
+ <div slot="supporting-text">
84
+ Device Type(s):
85
+ ${getEndpointDeviceTypes(this.node, this.endpoint)
86
+ .map(deviceType => {
87
+ return deviceType.label;
88
+ })
89
+ .join(" / ")}
90
+ </div>
91
+ </md-list-item>
92
+ ${guard([this.node?.attributes.length], () =>
93
+ getUniqueClusters(this.node!, this.endpoint!).map(cluster => {
94
+ return html`
95
+ <md-list-item
96
+ type="link"
97
+ href=${`#node/${this.node!.node_id}/${this.endpoint}/${cluster}`}
98
+ >
99
+ <div slot="headline">${clusters[cluster]?.label || "Custom/Unknown Cluster"}</div>
100
+ <div slot="supporting-text">ClusterId ${cluster} (0x00${cluster.toString(16)})</div>
101
+ <ha-svg-icon slot="end" .path=${mdiChevronRight}></ha-svg-icon>
102
+ </md-list-item>
103
+ `;
104
+ }),
105
+ )}
106
+ </md-list>
107
+ </div>
108
+ `;
109
+ }
110
+
111
+ private _goBack() {
112
+ history.back();
113
+ }
114
+
115
+ static override styles = css`
116
+ :host {
117
+ display: block;
118
+ background-color: var(--md-sys-color-background);
119
+ }
120
+
121
+ .header {
122
+ background-color: var(--md-sys-color-primary);
123
+ color: var(--md-sys-color-on-primary);
124
+ --icon-primary-color: var(--md-sys-color-on-primary);
125
+ font-weight: 400;
126
+ display: flex;
127
+ align-items: center;
128
+ padding-right: 8px;
129
+ height: 48px;
130
+ }
131
+
132
+ md-icon-button {
133
+ margin-right: 8px;
134
+ }
135
+
136
+ .flex {
137
+ flex: 1;
138
+ }
139
+
140
+ .container {
141
+ padding: 16px;
142
+ max-width: 95%;
143
+ margin: 0 auto;
144
+ }
145
+
146
+ .status {
147
+ color: var(--danger-color);
148
+ font-weight: bold;
149
+ font-size: 0.8em;
150
+ }
151
+ `;
152
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import "@material/web/divider/divider";
8
+ import "@material/web/iconbutton/icon-button";
9
+ import "@material/web/list/list";
10
+ import "@material/web/list/list-item";
11
+ import { MatterClient, MatterNode } from "@matter-server/ws-client";
12
+ import { mdiChevronRight } from "@mdi/js";
13
+ import { LitElement, css, html } from "lit";
14
+ import { customElement, property } from "lit/decorators.js";
15
+ import { guard } from "lit/directives/guard.js";
16
+ import "../components/ha-svg-icon";
17
+ import "./components/header";
18
+ import "./components/node-details";
19
+ import { getEndpointDeviceTypes } from "./matter-endpoint-view.js";
20
+
21
+ declare global {
22
+ interface HTMLElementTagNameMap {
23
+ "matter-node-view": MatterNodeView;
24
+ }
25
+ }
26
+
27
+ function getUniqueEndpoints(node: MatterNode) {
28
+ // extract unique endpoints from the node attributes, as (sorted) array
29
+ return Array.from(new Set(Object.keys(node.attributes).map(key => Number(key.split("/")[0])))).sort((a, b) => {
30
+ return a - b;
31
+ });
32
+ }
33
+
34
+ @customElement("matter-node-view")
35
+ class MatterNodeView extends LitElement {
36
+ public client!: MatterClient;
37
+
38
+ @property()
39
+ public node?: MatterNode;
40
+
41
+ override render() {
42
+ if (!this.node) {
43
+ return html`
44
+ <p>Node not found!</p>
45
+ <button @click=${this._goBack}>Back</button>
46
+ `;
47
+ }
48
+
49
+ return html`
50
+ <dashboard-header
51
+ .title=${"Node " + this.node.node_id}
52
+ .client=${this.client}
53
+ backButton="#"
54
+ ></dashboard-header>
55
+
56
+ <!-- node details section -->
57
+ <div class="container">
58
+ <node-details .node=${this.node} .client=${this.client}></node-details>
59
+ </div>
60
+
61
+ <!-- Node Endpoints listing -->
62
+ <div class="container">
63
+ <md-list>
64
+ <md-list-item>
65
+ <div slot="headline">
66
+ <b>Endpoints</b>
67
+ </div>
68
+ </md-list-item>
69
+ ${guard([this.node?.attributes.length], () =>
70
+ getUniqueEndpoints(this.node!).map(endPointId => {
71
+ return html`
72
+ <md-list-item type="link" href=${`#node/${this.node!.node_id}/${endPointId}`}>
73
+ <div slot="headline">Endpoint ${endPointId}</div>
74
+ <div slot="supporting-text">
75
+ Device Type(s):
76
+ ${getEndpointDeviceTypes(this.node!, endPointId)
77
+ .map(deviceType => {
78
+ return deviceType.label;
79
+ })
80
+ .join(" / ")}
81
+ </div>
82
+ <ha-svg-icon slot="end" .path=${mdiChevronRight}></ha-svg-icon>
83
+ </md-list-item>
84
+ `;
85
+ }),
86
+ )}
87
+ </md-list>
88
+ </div>
89
+
90
+ <dashboard-footer />
91
+ `;
92
+ }
93
+
94
+ private _goBack() {
95
+ history.back();
96
+ }
97
+
98
+ static override styles = css`
99
+ :host {
100
+ display: flex;
101
+ background-color: var(--md-sys-color-background);
102
+ box-sizing: border-box;
103
+ flex-direction: column;
104
+ min-height: 100vh;
105
+ }
106
+
107
+ .container {
108
+ padding: 16px;
109
+ max-width: 95%;
110
+ margin: 0 auto;
111
+ width: 100%;
112
+ }
113
+
114
+ @media (max-width: 600px) {
115
+ .container {
116
+ padding: 16px 0;
117
+ }
118
+ }
119
+
120
+ .status {
121
+ color: var(--danger-color);
122
+ font-weight: bold;
123
+ font-size: 0.8em;
124
+ }
125
+ `;
126
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import "@material/web/divider/divider";
8
+ import "@material/web/iconbutton/icon-button";
9
+ import "@material/web/list/list";
10
+ import "@material/web/list/list-item";
11
+ import { MatterClient, MatterNode } from "@matter-server/ws-client";
12
+ import { mdiChevronRight } from "@mdi/js";
13
+ import { LitElement, css, html, nothing } from "lit";
14
+ import { customElement, property } from "lit/decorators.js";
15
+ import "../components/ha-svg-icon";
16
+ import "./components/footer";
17
+ import "./components/header";
18
+ import "./components/server-details";
19
+
20
+ declare global {
21
+ interface HTMLElementTagNameMap {
22
+ "matter-server-view": MatterServerView;
23
+ }
24
+ }
25
+
26
+ @customElement("matter-server-view")
27
+ class MatterServerView extends LitElement {
28
+ public client!: MatterClient;
29
+
30
+ @property()
31
+ public nodes!: MatterClient["nodes"];
32
+
33
+ private _cachedNodes?: MatterClient["nodes"];
34
+ private _cachedNodeEntries?: [string, MatterNode][];
35
+
36
+ private getNodeEntries(nodes: MatterClient["nodes"]): [string, MatterNode][] {
37
+ if (nodes !== this._cachedNodes) {
38
+ this._cachedNodes = nodes;
39
+ this._cachedNodeEntries = Object.entries(nodes);
40
+ }
41
+ return this._cachedNodeEntries!;
42
+ }
43
+
44
+ override render() {
45
+ const nodes = this.getNodeEntries(this.nodes);
46
+
47
+ return html`
48
+ <dashboard-header title="Open Home Foundation Matter Server" .client=${this.client}></dashboard-header>
49
+
50
+ <!-- server details section -->
51
+ <div class="container">
52
+ <server-details .client=${this.client}></server-details>
53
+ </div>
54
+
55
+ <!-- Nodes listing -->
56
+ <div class="container">
57
+ <md-list>
58
+ <md-list-item>
59
+ <div slot="headline">
60
+ <b>Nodes</b>
61
+ </div>
62
+ </md-list-item>
63
+ ${nodes.map(([_id, node]) => {
64
+ return html`
65
+ <md-list-item type="link" href=${`#node/${node.node_id}`}>
66
+ <div slot="headline">
67
+ Node ${node.node_id}
68
+ ${node.available ? "" : html`<span class="status">OFFLINE</span>`}
69
+ </div>
70
+ <div slot="supporting-text">
71
+ ${node.nodeLabel ? `${node.nodeLabel} | ` : nothing} ${node.vendorName} |
72
+ ${node.productName}
73
+ </div>
74
+ <ha-svg-icon slot="end" .path=${mdiChevronRight}></ha-svg-icon>
75
+ </md-list-item>
76
+ `;
77
+ })}
78
+ </md-list>
79
+ </div>
80
+ <dashboard-footer />
81
+ `;
82
+ }
83
+
84
+ static override styles = css`
85
+ :host {
86
+ display: flex;
87
+ background-color: var(--md-sys-color-background);
88
+ box-sizing: border-box;
89
+ flex-direction: column;
90
+ }
91
+
92
+ .container {
93
+ padding: 16px;
94
+ max-width: 95%;
95
+ margin: 0 auto;
96
+ flex: 1;
97
+ width: 100%;
98
+ }
99
+
100
+ @media (max-width: 600px) {
101
+ .container {
102
+ padding: 16px 0;
103
+ }
104
+ }
105
+
106
+ span[slot="start"] {
107
+ width: 40px;
108
+ text-align: center;
109
+ }
110
+
111
+ .status {
112
+ color: var(--danger-color);
113
+ font-weight: bold;
114
+ font-size: 0.8em;
115
+ }
116
+ `;
117
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../tools/tsc/tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "types": [
5
+ "globals"
6
+ ],
7
+ "experimentalDecorators": true,
8
+ "useDefineForClassFields": false,
9
+ "allowSyntheticDefaultImports": true
10
+ },
11
+ "references": [
12
+ {
13
+ "path": "../../ws-client/src"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export const clone = (orig: any) => Object.assign(Object.create(Object.getPrototypeOf(orig)), orig);
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ // Polymer legacy event helpers used courtesy of the Polymer project.
8
+ //
9
+ // Copyright (c) 2017 The Polymer Authors. All rights reserved.
10
+ //
11
+ // Redistribution and use in source and binary forms, with or without
12
+ // modification, are permitted provided that the following conditions are
13
+ // met:
14
+ //
15
+ // * Redistributions of source code must retain the above copyright
16
+ // notice, this list of conditions and the following disclaimer.
17
+ // * Redistributions in binary form must reproduce the above
18
+ // copyright notice, this list of conditions and the following disclaimer
19
+ // in the documentation and/or other materials provided with the
20
+ // distribution.
21
+ // * Neither the name of Google Inc. nor the names of its
22
+ // contributors may be used to endorse or promote products derived from
23
+ // this software without specific prior written permission.
24
+ //
25
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
+ // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+ // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+ // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
+ // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+
37
+ declare global {
38
+ interface HASSDomEvents {}
39
+ }
40
+
41
+ export type ValidHassDomEvent = keyof HASSDomEvents;
42
+
43
+ export interface HASSDomEvent<T> extends Event {
44
+ detail: T;
45
+ }
46
+
47
+ /**
48
+ * Dispatches a custom event with an optional detail value.
49
+ *
50
+ * @param {string} type Name of event type.
51
+ * @param {*=} detail Detail value containing event-specific
52
+ * payload.
53
+ * @param {{ bubbles: (boolean|undefined),
54
+ * cancelable: (boolean|undefined),
55
+ * composed: (boolean|undefined) }=}
56
+ * options Object specifying options. These may include:
57
+ * `bubbles` (boolean, defaults to `true`),
58
+ * `cancelable` (boolean, defaults to false), and
59
+ * `node` on which to fire the event (HTMLElement, defaults to `this`).
60
+ * @return {Event} The new event that was fired.
61
+ */
62
+ export const fireEvent = <HassEvent extends ValidHassDomEvent>(
63
+ node: HTMLElement | Window,
64
+ type: HassEvent,
65
+ detail?: HASSDomEvents[HassEvent],
66
+ options?: {
67
+ bubbles?: boolean;
68
+ cancelable?: boolean;
69
+ composed?: boolean;
70
+ },
71
+ ) => {
72
+ options = options || {};
73
+ // @ts-expect-error why?
74
+ detail = detail === null || detail === undefined ? {} : detail;
75
+ const event = new Event(type, {
76
+ bubbles: options.bubbles === undefined ? true : options.bubbles,
77
+ cancelable: Boolean(options.cancelable),
78
+ composed: options.composed === undefined ? true : options.composed,
79
+ });
80
+ (event as any).detail = detail;
81
+ node.dispatchEvent(event);
82
+ return event;
83
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export const preventDefault = (ev: Event) => ev.preventDefault();
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export interface Route {
8
+ prefix: string;
9
+ path: string[];
10
+ }