/** * @license * Copyright 2025-2026 Open Home Foundation * SPDX-License-Identifier: Apache-2.0 */ import { provide } from "@lit/context"; import "@material/web/button/outlined-button"; import "@material/web/divider/divider"; import "@material/web/iconbutton/icon-button"; import "@material/web/list/list"; import "@material/web/list/list-item"; import { consume } from "@lit/context"; import { MatterClient, MatterNode, isTestNodeId } from "@matter-server/ws-client"; import { mdiAlertCircleOutline, mdiChevronRight } from "@mdi/js"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators.js"; import { guard } from "lit/directives/guard.js"; import "./cluster-commands/clusters/binding-commands.js"; import { clientContext, tickContext } from "../client/client-context.js"; import { clusters } from "../client/models/descriptions.js"; import "../components/ha-svg-icon"; import { getEndpointDeviceTypes } from "../util/endpoints.js"; import { formatHex, formatNodeAddress, getEffectiveFabricIndex } from "../util/format_hex.js"; import { notFoundStyles } from "../util/shared-styles.js"; import { bindingContext } from "./components/context.js"; declare global { interface HTMLElementTagNameMap { "matter-endpoint-view": MatterEndpointView; } } function getUniqueClusters(node: MatterNode, endpoint: number) { return Array.from( new Set( Object.keys(node.attributes) .filter(key => key.startsWith(`${endpoint.toString()}/`)) .map(key => Number(key.split("/")[1])), ), ).sort((a, b) => { return a - b; }); } export { getEndpointDeviceTypes }; @customElement("matter-endpoint-view") class MatterEndpointView extends LitElement { @consume({ context: clientContext }) public client!: MatterClient; @consume({ context: tickContext, subscribe: true }) protected _tick = 0; @property() public node?: MatterNode; @provide({ context: bindingContext }) @property() public endpoint!: number; override render() { if (!this.node || this.endpoint == undefined) { return html`

Node or endpoint not found

Back
`; } // Format node address for hex display const fabricIndex = getEffectiveFabricIndex( this.client.serverInfo.fabric_index, isTestNodeId(this.node.node_id), ); const nodeHex = formatNodeAddress(fabricIndex, this.node.node_id); return html`
${getUniqueClusters(this.node, this.endpoint).includes(30) ? html`
` : nothing}
Clusters on Endpoint ${this.endpoint}
Device Type(s): ${getEndpointDeviceTypes(this.node, this.endpoint) .map(deviceType => { return deviceType.label; }) .join(" / ")}
${guard([this.node?.attributes.length], () => getUniqueClusters(this.node!, this.endpoint).map(cluster => { return html`
${clusters[cluster]?.label ?? "Custom/Unknown Cluster"}
ClusterId ${cluster} (${formatHex(cluster)})
`; }), )}
`; } private _goBack() { history.back(); } static override styles = [ notFoundStyles, css` :host { display: block; background-color: var(--md-sys-color-background); } .header { background-color: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); --icon-primary-color: var(--md-sys-color-on-primary); font-weight: 400; display: flex; align-items: center; padding-right: 8px; height: 48px; } md-icon-button { margin-right: 8px; } .flex { flex: 1; } .container { padding: 16px; max-width: 95%; margin: 0 auto; } .status { color: var(--danger-color); font-weight: bold; font-size: 0.8em; } `, ]; }