/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import type { ClientNode } from "#node/ClientNode.js"; import type { ServerNode } from "#node/ServerNode.js"; import { Seconds, ServerAddress } from "@matter/general"; import { ControllerCommissioner, EstablishPaseOptions, EstablishPaseResult, NodeSession } from "@matter/protocol"; import { Discovery } from "./Discovery.js"; import { ParallelPaseDiscovery } from "./ParallelPaseDiscovery.js"; /** * Discovers commissionable devices and establishes a PASE session with the first one that responds. * * All discovered candidates attempt PASE in parallel; the first to succeed wins. Discovery stops at PASE * time and the abort signal fires to cancel remaining in-flight PASE attempts. Any candidate that * establishes PASE after the winner cleans up its session cleanly. {@link onComplete} returns the winning * {@link NodeSession}. * * This is the discovery counterpart to {@link CommissioningDiscovery}: whereas that class performs a full * commissioning flow, this class stops at PASE and returns the raw session for callers that manage their own * commissioning (e.g. split-commissioning scenarios or raw PASE channel establishment for chip-testing). * * Default discovery timeout is 60 seconds when not specified by the caller. */ export class PaseDiscovery extends ParallelPaseDiscovery { #options: PaseDiscovery.Options; constructor(owner: ServerNode, options: PaseDiscovery.Options) { if (options.timeout === undefined) { options = { ...options, timeout: Seconds(60) }; } super(owner, options); this.#options = options; } protected override get cleanupLabel() { return "PASE candidate cleanup"; } protected override get failureMessage() { return "No PASE session could be established"; } protected override onDiscovered(node: ClientNode) { if (this.paseWon) return; this.registerAttempt( winOnPase => node.act("establish-pase", agent => { const addresses = agent.commissioning.state.addresses; if (!addresses?.length) { return; } const commissioner = node.env.get(ControllerCommissioner); const establishOptions: EstablishPaseOptions = { addresses: addresses.map(ServerAddress), discoveryData: agent.commissioning.descriptor, passcode: this.#options.passcode, timeout: (this.#options as Discovery.InstanceOptions).timeout, abort: this.abortSignal, continueAfterPase: winOnPase, }; return commissioner.establishPase(establishOptions); }), result => result?.paseSession, ); } } export namespace PaseDiscovery { export type Options = Discovery.InstanceOptions & { /** PASE passcode for the device. */ passcode: number; }; }