src.nth.io/

summaryrefslogtreecommitdiff
path: root/src/device.ts
blob: 1c0d0eaebd6472e056837b6ef08a028a16c96fd2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import { NodeId } from "@matter/main";
import type { CommissioningController } from "@project-chip/matter.js";
import { NodeStates, type PairedNode } from "@project-chip/matter.js/device";
import { Log } from "./logging.js";

const log = new Log("device");

export function nodeStateName(state: NodeStates): string {
  switch (state) {
    case NodeStates.Connected:
      return "connected";
    case NodeStates.Disconnected:
      return "disconnected";
    case NodeStates.Reconnecting:
      return "reconnecting";
    case NodeStates.WaitingForDeviceDiscovery:
      return "waiting for device discovery";
  }
}

/**
 * Connects to the commissioned node using operational discovery (DNS-SD by
 * node identity, never a stored IP address) and waits until the node is fully
 * initialized from the device.
 *
 * The returned PairedNode keeps reconnecting in the background; observe
 * `events.stateChanged` for connectivity transitions.
 */
export async function connectDevice(
  controller: CommissioningController,
  nodeId: bigint,
  timeoutMs = 60_000,
): Promise<PairedNode> {
  const node = await controller.getNode(NodeId(nodeId));

  node.events.stateChanged.on(state => {
    log.debug(`Node ${nodeId} state: ${nodeStateName(state)}`);
  });

  if (!node.initialized) {
    log.info(`Connecting to Matter node ${nodeId}...`);
    node.connect();
    await waitForInitialization(node, nodeId, timeoutMs);
  } else if (!node.isConnected) {
    node.connect();
    await waitForInitialization(node, nodeId, timeoutMs);
  }

  log.info(`Connected to Matter node ${nodeId}`);
  return node;
}

async function waitForInitialization(node: PairedNode, nodeId: bigint, timeoutMs: number): Promise<void> {
  let timer: NodeJS.Timeout | undefined;
  const timeout = new Promise<never>((_, reject) => {
    timer = setTimeout(
      () =>
        reject(
          new Error(
            `Timed out after ${Math.round(timeoutMs / 1000)}s waiting for node ${nodeId}. ` +
              `The device may be offline or unreachable over IPv6.`,
          ),
        ),
      timeoutMs,
    );
  });
  try {
    await Promise.race([node.events.initializedFromRemote, timeout]);
  } finally {
    clearTimeout(timer);
  }
}