Skip to content

Commit

Permalink
Node delayed connection (#1619)
Browse files Browse the repository at this point in the history
* Allow delayed node connection for controller

In cases where a pairedNode should be initialized but not yet connected this PR adds new methods and properties to allow this.

* move a log line
  • Loading branch information
Apollon77 authored Jan 12, 2025
1 parent 3f4c88d commit 2972cf2
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 4 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ The main work (all changes without a GitHub username in brackets in the below li
- @project-chip/matter.js
- Feature: (Breaking) Added Fabric Label for Controller as required property to initialize the Controller
including setting the Fabric Label when commissioning and validating and updating the Fabric Label on
connection
connection
- Feature: Added autoConnect property to node connection options to allow to not automatically connect to a node when PairedNode instance is created. Also introduces a non-blocking PairedNode.connect() method to connect to a node
- Feature: Added CommissioningController.getNode() method to get a PairedNode instance for a node by its node ID without a direct connection
- Feature: Allows to update the Fabric Label during controller runtime using `updateFabricLabel()` on CommissioningController
- Enhancement: Improves Reconnection Handling for devices that use persisted subscriptions
- Enhancement: Use data type definitions from Model for Controller Device type definitions
Expand Down
12 changes: 11 additions & 1 deletion packages/matter.js/src/CommissioningController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,19 @@ export class CommissioningController extends MatterNode {
await this.controllerInstance?.disconnect(nodeId);
}

async getNode(nodeId: NodeId) {
const existingNode = this.initializedNodes.get(nodeId);
if (existingNode !== undefined) {
return existingNode;
}
return await this.connectNode(nodeId, { autoConnect: false });
}

/**
* Connect to an already paired Node.
* After connection the endpoint data of the device is analyzed and an object structure is created.
* This call is not blocking and returns an initialized PairedNode instance. The connection or reconnection
* happens in the background. Please monitor the state of the node to see if the connection was successful.
*/
async connectNode(nodeId: NodeId, connectOptions?: CommissioningControllerNodeOptions) {
const controller = this.assertControllerIsStarted();
Expand All @@ -333,7 +343,7 @@ export class CommissioningController extends MatterNode {
const existingNode = this.initializedNodes.get(nodeId);
if (existingNode !== undefined) {
if (!existingNode.initialized) {
await existingNode.reconnect(connectOptions);
existingNode.connect(connectOptions);
}
return existingNode;
}
Expand Down
20 changes: 19 additions & 1 deletion packages/matter.js/src/device/PairedNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ export enum NodeStateInformation {
}

export type CommissioningControllerNodeOptions = {
/**
* Unless set to false the node will be automatically connected when initialized. When set to false use
* connect() to connect to the node at a later timepoint.
*/
readonly autoConnect?: boolean;

/**
* Unless set to false all events and attributes are subscribed and value changes are reflected in the ClusterClient
* instances. With this reading attributes values is mostly looked up in the locally cached data.
Expand Down Expand Up @@ -452,6 +458,18 @@ export class PairedNode {
}
}

/**
* Schedule a connection to the device. This method is non-blocking and will return immediately.
* The connection happens in the background. Please monitor the state of the node to see if the
* connection was successful.
*/
connect(connectOptions?: CommissioningControllerNodeOptions) {
if (connectOptions !== undefined) {
this.options = connectOptions;
}
this.triggerReconnect();
}

/**
* Trigger a reconnection to the device. This method is non-blocking and will return immediately.
* The reconnection happens in the background. Please monitor the state of the node to see if the
Expand All @@ -460,7 +478,7 @@ export class PairedNode {
triggerReconnect() {
if (this.#reconnectionInProgress || this.#remoteInitializationInProgress) {
logger.info(
`Node ${this.nodeId}: Ignoring reconnect request because ${this.#remoteInitializationInProgress ? "init" : "reconnect"} already underway.`,
`Node ${this.nodeId}: Ignoring reconnect request because ${this.#remoteInitializationInProgress ? "initialization" : "reconnect"} already in progress.`,
);
return;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/src/protocol/ExchangeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,8 @@ export class ExchangeManager {
logger.debug(`Channel for session ${session.name} is ${channel?.name}`);
if (channel !== undefined) {
const exchange = this.initiateExchangeWithChannel(channel, SECURE_CHANNEL_PROTOCOL_ID);
logger.debug(`Initiated exchange ${!!exchange} to close session ${sessionName}`);
if (exchange !== undefined) {
logger.debug(`Initiated exchange ${exchange.id} to close session ${sessionName}`);
try {
const messenger = new SecureChannelMessenger(exchange);
await messenger.sendCloseSession();
Expand Down

0 comments on commit 2972cf2

Please sign in to comment.