@pmouli/isy-matter-server
Version:
Service to expose an ISY device as a Matter Border router
92 lines • 3.95 kB
JavaScript
/**
* @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