/** * @license * Copyright 2025-2026 Open Home Foundation * SPDX-License-Identifier: Apache-2.0 */ import { consume } from "@lit/context"; import "@material/web/progress/circular-progress"; import "@material/web/textfield/outlined-text-field"; import type { MdOutlinedTextField } from "@material/web/textfield/outlined-text-field.js"; import { MatterClient } from "@matter-server/ws-client"; import { mdiAccessPoint } from "@mdi/js"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators.js"; import { clientContext } from "../../../client/client-context.js"; import { handleAsync } from "../../../util/async-handler.js"; import { fireEvent } from "../../../util/fire_event.js"; import { showAlertDialog } from "../../dialog-box/show-dialog-box.js"; import "../../ha-svg-icon.js"; @customElement("commission-node-thread") export class CommissionNodeThread extends LitElement { static override styles = css` .cred-chip { display: flex; width: fit-content; align-items: center; gap: 6px; background: var(--md-sys-color-surface-container); color: var(--md-sys-color-on-surface-variant); border-radius: 16px; padding: 4px 10px 4px 6px; font-size: 0.85em; margin-bottom: 12px; } .cred-chip ha-svg-icon { width: 18px; height: 18px; flex-shrink: 0; } .cred-chip .sep { opacity: 0.5; } .cred-chip .edit-link { cursor: pointer; color: var(--md-sys-color-primary); background: none; border: none; padding: 0; font: inherit; font-size: inherit; } `; @consume({ context: clientContext, subscribe: true }) @property({ attribute: false }) public client!: MatterClient; @state() private _loading: boolean = false; @query("md-outlined-text-field[label='Thread dataset']") private _datasetField!: MdOutlinedTextField; @query("md-outlined-text-field[label='Pairing code']") private _pairingCodeField!: MdOutlinedTextField; private _pairingFocused = false; private _credsFocused = false; protected override updated(): void { void this._maybeAutofocus().catch(err => console.warn("Autofocus failed:", err)); } private async _maybeAutofocus(): Promise { if (this._pairingCodeField && !this._pairingFocused) { this._pairingFocused = true; await this._pairingCodeField.updateComplete; this._pairingCodeField.focus(); } else if (this._datasetField && !this._credsFocused) { this._credsFocused = true; await this._datasetField.updateComplete; this._datasetField.focus(); } } protected override render() { if (!this.client.serverInfo.thread_credentials_set) { return html`

this._setThreadDataset())} .disabled="${this._loading}" >Set Thread Dataset${this._loading ? html` ` : nothing}`; } return html`
Thread network set ยท


this._commissionNode())} .disabled="${this._loading}" >Commission${this._loading ? html` ` : nothing}`; } private async _setThreadDataset() { const dataset = this._datasetField.value.trim(); if (!dataset) { showAlertDialog({ title: "Validation error", text: "Dataset is required" }); return; } if (!/^[0-9a-fA-F]*$/.test(dataset) || dataset.length % 2 !== 0) { showAlertDialog({ title: "Invalid Thread dataset", text: "Must be a hex string with even length (each byte is two hex characters)", }); return; } this._loading = true; try { await this.client.setThreadOperationalDataset(dataset); } catch (err) { showAlertDialog({ title: "Error setting Thread dataset", text: (err as Error).message }); } finally { this._loading = false; } } private async _commissionNode() { this._loading = true; try { const node = await this.client.commissionWithCode(this._pairingCodeField.value, false); fireEvent(this, "node-commissioned", node); } catch (err) { showAlertDialog({ title: "Error commissioning node", text: (err as Error).message }); } finally { this._loading = false; } } } declare global { interface HASSDomEvents { "request-settings": Record; } }