UNPKG

@pmouli/isy-matter-server

Version:

Service to expose an ISY device as a Matter Border router

92 lines 3.95 kB
/** * @license * Copyright 2022-2025 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import { Lifecycle, UnsupportedDependencyError } from '@matter/general'; import { FabricManager, FailsafeContext } from '@matter/main/protocol'; import { NetworkCommissioningBehavior } from '@matter/node/behaviors/network-commissioning'; /** * {@link FailsafeContext} for {@link ServerNode} API. */ export class ServerNodeFailsafeContext extends FailsafeContext { #node; #storedState; constructor(node, options) { super(options); this.#node = node; this.#node.env.set(FailsafeContext, this); this.construction.change.on((status) => { if (status === Lifecycle.Status.Destroyed) { this.#node.env.delete(FailsafeContext, this); } }); } /** * Persist endpoint credentials and network configurations for restoration if commissioning does not complete. * * The Matter 1.2 specification makes it pretty clear that Matter supports configuration of multiple network * interfaces (e.g. @see {@link MatterSpecification.v11.Core} § 11.8.8 and § 2.3.2). * {@link NetworkCommissioningCluster} of the primary interface is on the root endpoint. However it's not clear * where {@link NetworkCommissioningCluster} instances for secondary interfaces reside. To be on the safe side * we just assume any endpoint may support {@link NetworkCommissioningCluster}. * * TODO - it's recommended to reset all state if commissioning bails; currently we perform mandatory restore */ async storeEndpointState() { // const opcreds = this.#node.state.operationalCredentials; this.#storedState = { networks: new Map() /* nocs: opcreds.nocs.map(noc => ({ ...noc })), fabrics: opcreds.fabrics.map(fabric => ({ ...fabric })), trustedRootCertificates: [...opcreds.trustedRootCertificates], */ }; if (!this.#node.behaviors.has(NetworkCommissioningBehavior)) { return; } this.#node.visit((endpoint) => { if (endpoint.behaviors.has(NetworkCommissioningBehavior)) { this.#storedState?.networks.set(endpoint, endpoint.stateOf(NetworkCommissioningBehavior).networks); } }); } async restoreNetworkState() { await this.#node.act(this.restoreNetworkState.name, async (agent) => { const context = agent.context; await this.#node.visit(async (endpoint) => { const networks = this.#storedState?.networks.get(endpoint); if (networks) { context.agentFor(endpoint).get(NetworkCommissioningBehavior).state.networks = [...networks]; } }); }); } async revokeFabric(fabric) { await fabric.remove(); // await this.#restoreOperationalCredentials(); } async restoreBreadcrumb() { await this.#node.act(this.restoreBreadcrumb.name, (agent) => { agent.generalCommissioning.state.breadcrumb = 0; }); } async rollback() { if (!this.fabricIndex && this.hasRootCert) { // Update the fabric details if needed (like Trusted Root certificates) Only if fabric was not added because // else all data gets updated anyway try { const fabricManager = this.#node.env.get(FabricManager); fabricManager.events.failsafeClosed.emit(); } catch (error) { // UnsupportedDependencyError can happen when the node closes. Then data are refreshed on next start // anyway, so ignore this case UnsupportedDependencyError.accept(error); } } return super.rollback(); } } //# sourceMappingURL=ServerNodeFailsafeContext.js.map