@matter/protocol 0.12.4-alpha.0-20250223-1e0341a1a → 0.12.4-alpha.0-20250224-e0964a795

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/cjs/action/client/ClientInteraction.d.ts +38 -0
  2. package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -0
  3. package/dist/cjs/action/client/ClientInteraction.js +91 -0
  4. package/dist/cjs/action/client/ClientInteraction.js.map +6 -0
  5. package/dist/cjs/action/client/index.d.ts +7 -0
  6. package/dist/cjs/action/client/index.d.ts.map +1 -0
  7. package/dist/cjs/action/client/index.js +24 -0
  8. package/dist/cjs/action/client/index.js.map +6 -0
  9. package/dist/cjs/action/index.d.ts +1 -0
  10. package/dist/cjs/action/index.d.ts.map +1 -1
  11. package/dist/cjs/action/index.js +1 -0
  12. package/dist/cjs/action/index.js.map +1 -1
  13. package/dist/cjs/interaction/DecodedDataReport.d.ts +15 -0
  14. package/dist/cjs/interaction/DecodedDataReport.d.ts.map +1 -0
  15. package/dist/cjs/interaction/DecodedDataReport.js +42 -0
  16. package/dist/cjs/interaction/DecodedDataReport.js.map +6 -0
  17. package/dist/cjs/interaction/InteractionClient.d.ts +13 -23
  18. package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
  19. package/dist/cjs/interaction/InteractionClient.js +99 -127
  20. package/dist/cjs/interaction/InteractionClient.js.map +1 -1
  21. package/dist/cjs/interaction/InteractionMessenger.d.ts +94 -1
  22. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  23. package/dist/cjs/interaction/InteractionMessenger.js +56 -37
  24. package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
  25. package/dist/cjs/interaction/InteractionServer.d.ts +4 -2
  26. package/dist/cjs/interaction/InteractionServer.d.ts.map +1 -1
  27. package/dist/cjs/interaction/InteractionServer.js +12 -4
  28. package/dist/cjs/interaction/InteractionServer.js.map +1 -1
  29. package/dist/cjs/interaction/SubscriptionClient.d.ts +38 -0
  30. package/dist/cjs/interaction/SubscriptionClient.d.ts.map +1 -0
  31. package/dist/cjs/interaction/SubscriptionClient.js +98 -0
  32. package/dist/cjs/interaction/SubscriptionClient.js.map +6 -0
  33. package/dist/cjs/interaction/index.d.ts +1 -0
  34. package/dist/cjs/interaction/index.d.ts.map +1 -1
  35. package/dist/cjs/interaction/index.js +1 -0
  36. package/dist/cjs/interaction/index.js.map +1 -1
  37. package/dist/cjs/peer/ControllerCommissioner.d.ts +2 -2
  38. package/dist/cjs/peer/ControllerCommissioner.d.ts.map +1 -1
  39. package/dist/cjs/peer/ControllerCommissioner.js +4 -3
  40. package/dist/cjs/peer/ControllerCommissioner.js.map +1 -1
  41. package/dist/cjs/peer/InteractionQueue.d.ts +11 -0
  42. package/dist/cjs/peer/InteractionQueue.d.ts.map +1 -0
  43. package/dist/cjs/peer/InteractionQueue.js +42 -0
  44. package/dist/cjs/peer/InteractionQueue.js.map +6 -0
  45. package/dist/cjs/peer/PeerAddressStore.d.ts +1 -1
  46. package/dist/cjs/peer/PeerAddressStore.d.ts.map +1 -1
  47. package/dist/cjs/peer/PeerSet.d.ts +16 -7
  48. package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
  49. package/dist/cjs/peer/PeerSet.js +58 -61
  50. package/dist/cjs/peer/PeerSet.js.map +1 -1
  51. package/dist/cjs/peer/PhysicalDeviceProperties.d.ts +26 -0
  52. package/dist/cjs/peer/PhysicalDeviceProperties.d.ts.map +1 -0
  53. package/dist/cjs/peer/PhysicalDeviceProperties.js +74 -0
  54. package/dist/cjs/peer/PhysicalDeviceProperties.js.map +6 -0
  55. package/dist/cjs/peer/index.d.ts +1 -0
  56. package/dist/cjs/peer/index.d.ts.map +1 -1
  57. package/dist/cjs/peer/index.js +1 -0
  58. package/dist/cjs/peer/index.js.map +1 -1
  59. package/dist/cjs/protocol/ExchangeManager.d.ts +1 -0
  60. package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
  61. package/dist/cjs/protocol/ExchangeManager.js +6 -3
  62. package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
  63. package/dist/cjs/protocol/ExchangeProvider.d.ts +13 -1
  64. package/dist/cjs/protocol/ExchangeProvider.d.ts.map +1 -1
  65. package/dist/cjs/protocol/ExchangeProvider.js +2 -0
  66. package/dist/cjs/protocol/ExchangeProvider.js.map +1 -1
  67. package/dist/cjs/protocol/ProtocolHandler.d.ts +1 -1
  68. package/dist/cjs/protocol/ProtocolHandler.d.ts.map +1 -1
  69. package/dist/cjs/securechannel/SecureChannelProtocol.d.ts +1 -1
  70. package/dist/cjs/securechannel/SecureChannelProtocol.d.ts.map +1 -1
  71. package/dist/cjs/securechannel/SecureChannelProtocol.js +1 -3
  72. package/dist/cjs/securechannel/SecureChannelProtocol.js.map +1 -1
  73. package/dist/cjs/session/SessionManager.d.ts.map +1 -1
  74. package/dist/cjs/session/SessionManager.js +1 -0
  75. package/dist/cjs/session/SessionManager.js.map +1 -1
  76. package/dist/cjs/session/case/CaseServer.d.ts +1 -1
  77. package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
  78. package/dist/cjs/session/case/CaseServer.js +1 -3
  79. package/dist/cjs/session/case/CaseServer.js.map +1 -1
  80. package/dist/cjs/session/pase/PaseServer.d.ts +2 -3
  81. package/dist/cjs/session/pase/PaseServer.d.ts.map +1 -1
  82. package/dist/cjs/session/pase/PaseServer.js +12 -14
  83. package/dist/cjs/session/pase/PaseServer.js.map +1 -1
  84. package/dist/esm/action/client/ClientInteraction.d.ts +38 -0
  85. package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -0
  86. package/dist/esm/action/client/ClientInteraction.js +71 -0
  87. package/dist/esm/action/client/ClientInteraction.js.map +6 -0
  88. package/dist/esm/action/client/index.d.ts +7 -0
  89. package/dist/esm/action/client/index.d.ts.map +1 -0
  90. package/dist/esm/action/client/index.js +7 -0
  91. package/dist/esm/action/client/index.js.map +6 -0
  92. package/dist/esm/action/index.d.ts +1 -0
  93. package/dist/esm/action/index.d.ts.map +1 -1
  94. package/dist/esm/action/index.js +1 -0
  95. package/dist/esm/action/index.js.map +1 -1
  96. package/dist/esm/interaction/DecodedDataReport.d.ts +15 -0
  97. package/dist/esm/interaction/DecodedDataReport.d.ts.map +1 -0
  98. package/dist/esm/interaction/DecodedDataReport.js +22 -0
  99. package/dist/esm/interaction/DecodedDataReport.js.map +6 -0
  100. package/dist/esm/interaction/InteractionClient.d.ts +13 -23
  101. package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
  102. package/dist/esm/interaction/InteractionClient.js +101 -134
  103. package/dist/esm/interaction/InteractionClient.js.map +1 -1
  104. package/dist/esm/interaction/InteractionMessenger.d.ts +94 -1
  105. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  106. package/dist/esm/interaction/InteractionMessenger.js +56 -37
  107. package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
  108. package/dist/esm/interaction/InteractionServer.d.ts +4 -2
  109. package/dist/esm/interaction/InteractionServer.d.ts.map +1 -1
  110. package/dist/esm/interaction/InteractionServer.js +12 -4
  111. package/dist/esm/interaction/InteractionServer.js.map +1 -1
  112. package/dist/esm/interaction/SubscriptionClient.d.ts +38 -0
  113. package/dist/esm/interaction/SubscriptionClient.d.ts.map +1 -0
  114. package/dist/esm/interaction/SubscriptionClient.js +78 -0
  115. package/dist/esm/interaction/SubscriptionClient.js.map +6 -0
  116. package/dist/esm/interaction/index.d.ts +1 -0
  117. package/dist/esm/interaction/index.d.ts.map +1 -1
  118. package/dist/esm/interaction/index.js +1 -0
  119. package/dist/esm/interaction/index.js.map +1 -1
  120. package/dist/esm/peer/ControllerCommissioner.d.ts +2 -2
  121. package/dist/esm/peer/ControllerCommissioner.d.ts.map +1 -1
  122. package/dist/esm/peer/ControllerCommissioner.js +6 -5
  123. package/dist/esm/peer/ControllerCommissioner.js.map +1 -1
  124. package/dist/esm/peer/InteractionQueue.d.ts +11 -0
  125. package/dist/esm/peer/InteractionQueue.d.ts.map +1 -0
  126. package/dist/esm/peer/InteractionQueue.js +22 -0
  127. package/dist/esm/peer/InteractionQueue.js.map +6 -0
  128. package/dist/esm/peer/PeerAddressStore.d.ts +1 -1
  129. package/dist/esm/peer/PeerAddressStore.d.ts.map +1 -1
  130. package/dist/esm/peer/PeerSet.d.ts +16 -7
  131. package/dist/esm/peer/PeerSet.d.ts.map +1 -1
  132. package/dist/esm/peer/PeerSet.js +59 -62
  133. package/dist/esm/peer/PeerSet.js.map +1 -1
  134. package/dist/esm/peer/PhysicalDeviceProperties.d.ts +26 -0
  135. package/dist/esm/peer/PhysicalDeviceProperties.d.ts.map +1 -0
  136. package/dist/esm/peer/PhysicalDeviceProperties.js +54 -0
  137. package/dist/esm/peer/PhysicalDeviceProperties.js.map +6 -0
  138. package/dist/esm/peer/index.d.ts +1 -0
  139. package/dist/esm/peer/index.d.ts.map +1 -1
  140. package/dist/esm/peer/index.js +1 -0
  141. package/dist/esm/peer/index.js.map +1 -1
  142. package/dist/esm/protocol/ExchangeManager.d.ts +1 -0
  143. package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
  144. package/dist/esm/protocol/ExchangeManager.js +6 -3
  145. package/dist/esm/protocol/ExchangeManager.js.map +1 -1
  146. package/dist/esm/protocol/ExchangeProvider.d.ts +13 -1
  147. package/dist/esm/protocol/ExchangeProvider.d.ts.map +1 -1
  148. package/dist/esm/protocol/ExchangeProvider.js +2 -0
  149. package/dist/esm/protocol/ExchangeProvider.js.map +1 -1
  150. package/dist/esm/protocol/ProtocolHandler.d.ts +1 -1
  151. package/dist/esm/protocol/ProtocolHandler.d.ts.map +1 -1
  152. package/dist/esm/securechannel/SecureChannelProtocol.d.ts +1 -1
  153. package/dist/esm/securechannel/SecureChannelProtocol.d.ts.map +1 -1
  154. package/dist/esm/securechannel/SecureChannelProtocol.js +1 -3
  155. package/dist/esm/securechannel/SecureChannelProtocol.js.map +1 -1
  156. package/dist/esm/session/SessionManager.d.ts.map +1 -1
  157. package/dist/esm/session/SessionManager.js +1 -0
  158. package/dist/esm/session/SessionManager.js.map +1 -1
  159. package/dist/esm/session/case/CaseServer.d.ts +1 -1
  160. package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
  161. package/dist/esm/session/case/CaseServer.js +1 -3
  162. package/dist/esm/session/case/CaseServer.js.map +1 -1
  163. package/dist/esm/session/pase/PaseServer.d.ts +2 -3
  164. package/dist/esm/session/pase/PaseServer.d.ts.map +1 -1
  165. package/dist/esm/session/pase/PaseServer.js +12 -14
  166. package/dist/esm/session/pase/PaseServer.js.map +1 -1
  167. package/package.json +6 -6
  168. package/src/action/client/ClientInteraction.ts +110 -0
  169. package/src/action/client/index.ts +7 -0
  170. package/src/action/index.ts +1 -0
  171. package/src/interaction/DecodedDataReport.ts +29 -0
  172. package/src/interaction/InteractionClient.ts +112 -164
  173. package/src/interaction/InteractionMessenger.ts +63 -43
  174. package/src/interaction/InteractionServer.ts +18 -5
  175. package/src/interaction/SubscriptionClient.ts +107 -0
  176. package/src/interaction/index.ts +1 -0
  177. package/src/peer/ControllerCommissioner.ts +7 -6
  178. package/src/peer/InteractionQueue.ts +22 -0
  179. package/src/peer/PeerAddressStore.ts +1 -1
  180. package/src/peer/PeerSet.ts +69 -76
  181. package/src/peer/PhysicalDeviceProperties.ts +80 -0
  182. package/src/peer/index.ts +1 -0
  183. package/src/protocol/ExchangeManager.ts +7 -3
  184. package/src/protocol/ExchangeProvider.ts +14 -1
  185. package/src/protocol/ProtocolHandler.ts +1 -1
  186. package/src/securechannel/SecureChannelProtocol.ts +1 -3
  187. package/src/session/SessionManager.ts +1 -0
  188. package/src/session/case/CaseServer.ts +2 -4
  189. package/src/session/pase/PaseServer.ts +13 -15
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Interactable } from "#action/Interactable.js";
8
+ import { Invoke } from "#action/request/Invoke.js";
9
+ import { Read } from "#action/request/Read.js";
10
+ import { Subscribe } from "#action/request/Subscribe.js";
11
+ import { Write } from "#action/request/Write.js";
12
+ import { InvokeResult } from "#action/response/InvokeResult.js";
13
+ import { ReadResult } from "#action/response/ReadResult.js";
14
+ import { SubscribeResult } from "#action/response/SubscribeResult.js";
15
+ import { WriteResult } from "#action/response/WriteResult.js";
16
+ import { AccessControl } from "#action/server/AccessControl.js";
17
+ import { CancelablePromise, Environment, Environmental, NotImplementedError, PromiseQueue } from "#general";
18
+ import { InteractionClientMessenger } from "#interaction/InteractionMessenger.js";
19
+ import { SubscriptionClient } from "#interaction/SubscriptionClient.js";
20
+ import { InteractionQueue } from "#peer/InteractionQueue.js";
21
+ import { ExchangeProvider } from "#protocol/ExchangeProvider.js";
22
+ import { WriteResponse } from "#types";
23
+
24
+ export interface ClientInteractableContext {
25
+ exchanges: ExchangeProvider;
26
+ subscriptions: SubscriptionClient;
27
+ queue: PromiseQueue;
28
+ }
29
+
30
+ /**
31
+ * This is a WIP and currently largely a stub.
32
+ */
33
+ export class ClientInteraction<SessionT extends AccessControl.Session = AccessControl.Session>
34
+ implements Interactable<SessionT>
35
+ {
36
+ readonly #exchanges: ExchangeProvider;
37
+ readonly #subscriptions: SubscriptionClient;
38
+ readonly #queue?: PromiseQueue;
39
+
40
+ constructor(context: ClientInteractableContext) {
41
+ this.#exchanges = context.exchanges;
42
+ this.#subscriptions = context.subscriptions;
43
+ this.#queue = context.queue;
44
+ }
45
+
46
+ get subscriptions() {
47
+ return this.#subscriptions;
48
+ }
49
+
50
+ get queue() {
51
+ return this.#queue;
52
+ }
53
+
54
+ static [Environmental.create](env: Environment) {
55
+ const instance = new ClientInteraction({
56
+ exchanges: env.get(ExchangeProvider),
57
+ subscriptions: env.get(SubscriptionClient),
58
+ queue: env.get(InteractionQueue),
59
+ });
60
+ env.set(ClientInteraction, instance);
61
+ return instance;
62
+ }
63
+
64
+ read(_request: Read, _session?: SessionT): ReadResult {
65
+ // TODO
66
+ throw new NotImplementedError();
67
+ }
68
+
69
+ write<T extends Write>(request: T, _session?: SessionT) {
70
+ return new CancelablePromise<void | WriteResponse>((resolve, reject) => {
71
+ InteractionClientMessenger.create(this.#exchanges).then(messenger => {
72
+ const send = messenger.sendWriteCommand(request);
73
+
74
+ let sendResolve;
75
+ if (request.suppressResponse) {
76
+ sendResolve = send.then(() => {
77
+ resolve();
78
+ });
79
+ } else {
80
+ sendResolve = send.then(resolve);
81
+ }
82
+
83
+ sendResolve.catch(reject);
84
+ }, reject);
85
+ }) as WriteResult<T>;
86
+ }
87
+
88
+ invoke<T extends Invoke>(request: Invoke, _session?: SessionT): InvokeResult<T> {
89
+ if (request.suppressResponse) {
90
+ return new CancelablePromise<void>((resolve, reject) => {
91
+ InteractionClientMessenger.create(this.#exchanges)
92
+ .then(messenger =>
93
+ messenger
94
+ .sendInvokeCommand(request)
95
+ .then(() => resolve())
96
+ .catch(reject),
97
+ )
98
+ .then(resolve, reject);
99
+ }) as InvokeResult<T>;
100
+ }
101
+
102
+ // TODO
103
+ throw new NotImplementedError();
104
+ }
105
+
106
+ subscribe(_request: Subscribe, _session?: SessionT): SubscribeResult {
107
+ // TODO
108
+ throw new NotImplementedError();
109
+ }
110
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Project CHIP Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./ClientInteraction.js";
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ export * from "./client/index.js";
7
8
  export * from "./errors.js";
8
9
  export * from "./Interactable.js";
9
10
  export * from "./protocols.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { DataReport } from "#types";
8
+ import { DecodedAttributeReportValue, normalizeAndDecodeReadAttributeReport } from "./AttributeDataDecoder.js";
9
+ import { DecodedEventReportValue, normalizeAndDecodeReadEventReport } from "./EventDataDecoder.js";
10
+
11
+ export interface DecodedDataReport extends DataReport {
12
+ isNormalized: true;
13
+ attributeReports: DecodedAttributeReportValue<any>[];
14
+ eventReports: DecodedEventReportValue<any>[];
15
+ }
16
+
17
+ export function DecodedDataReport(report: DataReport): DecodedDataReport {
18
+ if ((report as DecodedDataReport).isNormalized) {
19
+ return report as DecodedDataReport;
20
+ }
21
+
22
+ return {
23
+ ...report,
24
+ isNormalized: true,
25
+ attributeReports:
26
+ report.attributeReports === undefined ? [] : normalizeAndDecodeReadAttributeReport(report.attributeReports),
27
+ eventReports: report.eventReports === undefined ? [] : normalizeAndDecodeReadEventReport(report.eventReports),
28
+ };
29
+ }
@@ -5,20 +5,20 @@
5
5
  */
6
6
 
7
7
  import {
8
+ Environment,
9
+ Environmental,
8
10
  ImplementationError,
9
- InternalError,
10
11
  Logger,
11
12
  MatterFlowError,
12
- MaybePromise,
13
13
  PromiseQueue,
14
- Time,
15
14
  Timer,
16
15
  UnexpectedDataError,
17
16
  isDeepEqual,
18
17
  } from "#general";
19
18
  import { Specification } from "#model";
20
- import { PeerAddress } from "#peer/PeerAddress.js";
19
+ import { PeerAddress, PeerAddressMap } from "#peer/PeerAddress.js";
21
20
  import { PeerDataStore } from "#peer/PeerAddressStore.js";
21
+ import { DiscoveryOptions, PeerSet } from "#peer/PeerSet.js";
22
22
  import {
23
23
  ArraySchema,
24
24
  Attribute,
@@ -30,7 +30,6 @@ import {
30
30
  Event,
31
31
  EventId,
32
32
  EventNumber,
33
- INTERACTION_PROTOCOL_ID,
34
33
  NodeId,
35
34
  RequestType,
36
35
  ResponseType,
@@ -47,16 +46,11 @@ import {
47
46
  resolveEventName,
48
47
  } from "#types";
49
48
  import { ExchangeProvider, ReconnectableExchangeProvider } from "../protocol/ExchangeProvider.js";
50
- import { MessageExchange } from "../protocol/MessageExchange.js";
51
- import { ProtocolHandler } from "../protocol/ProtocolHandler.js";
52
- import { DecodedAttributeReportValue, normalizeAndDecodeReadAttributeReport } from "./AttributeDataDecoder.js";
53
- import { DecodedEventData, DecodedEventReportValue, normalizeAndDecodeReadEventReport } from "./EventDataDecoder.js";
54
- import {
55
- DataReport,
56
- IncomingInteractionClientMessenger,
57
- InteractionClientMessenger,
58
- ReadRequest,
59
- } from "./InteractionMessenger.js";
49
+ import { DecodedAttributeReportValue } from "./AttributeDataDecoder.js";
50
+ import { DecodedDataReport } from "./DecodedDataReport.js";
51
+ import { DecodedEventData, DecodedEventReportValue } from "./EventDataDecoder.js";
52
+ import { DataReport, InteractionClientMessenger, ReadRequest } from "./InteractionMessenger.js";
53
+ import { RegisteredSubscription, SubscriptionClient } from "./SubscriptionClient.js";
60
54
 
61
55
  const logger = Logger.get("InteractionClient");
62
56
 
@@ -74,102 +68,98 @@ export interface AttributeStatus {
74
68
  status: StatusCode;
75
69
  }
76
70
 
77
- export class SubscriptionClient implements ProtocolHandler {
78
- private readonly subscriptionListeners = new Map<number, (dataReport: DataReport) => MaybePromise<void>>();
79
- private readonly subscriptionUpdateTimers = new Map<number, Timer>();
80
-
81
- constructor() {}
71
+ export class InteractionClientProvider {
72
+ readonly #peers: PeerSet;
73
+ readonly #clients = new PeerAddressMap<InteractionClient>();
82
74
 
83
- getId() {
84
- return INTERACTION_PROTOCOL_ID;
75
+ constructor(peers: PeerSet) {
76
+ this.#peers = peers;
77
+ this.#peers.deleted.on(peer => this.#onPeerLoss(peer.address));
78
+ this.#peers.disconnected.on(address => this.#onPeerLoss(address));
85
79
  }
86
80
 
87
- registerSubscriptionListener(subscriptionId: number, listener: (dataReport: DataReport) => MaybePromise<void>) {
88
- this.subscriptionListeners.set(subscriptionId, listener);
81
+ static [Environmental.create](env: Environment) {
82
+ const instance = new InteractionClientProvider(env.get(PeerSet));
83
+ env.set(InteractionClientProvider, instance);
84
+ return instance;
89
85
  }
90
86
 
91
- removeSubscriptionListener(subscriptionId: number) {
92
- this.subscriptionListeners.delete(subscriptionId);
87
+ get peers() {
88
+ return this.#peers;
93
89
  }
94
90
 
95
- registerSubscriptionUpdateTimer(subscriptionId: number, timer: Timer) {
96
- this.subscriptionUpdateTimers.set(subscriptionId, timer);
97
- }
91
+ async connect(
92
+ address: PeerAddress,
93
+ discoveryOptions: DiscoveryOptions,
94
+ allowUnknownPeer = false,
95
+ ): Promise<InteractionClient> {
96
+ await this.#peers.ensureConnection(address, discoveryOptions, allowUnknownPeer);
98
97
 
99
- removeSubscriptionUpdateTimer(subscriptionId: number) {
100
- this.subscriptionUpdateTimers.get(subscriptionId)?.stop();
101
- this.subscriptionUpdateTimers.delete(subscriptionId);
98
+ return this.getInteractionClient(address, discoveryOptions);
102
99
  }
103
100
 
104
- async onNewExchange(exchange: MessageExchange) {
105
- const messenger = new IncomingInteractionClientMessenger(exchange);
106
-
107
- let dataReport: DataReport;
108
- try {
109
- // TODO Adjust this to getting packages as callback when received to handle error cases and checks outside
110
- dataReport = await messenger.readDataReports([...this.subscriptionListeners.keys()]);
111
- } finally {
112
- messenger.close().catch(error => logger.info("Error closing client messenger", error));
101
+ async getInteractionClient(address: PeerAddress, discoveryOptions: DiscoveryOptions) {
102
+ let client = this.#clients.get(address);
103
+ if (client !== undefined) {
104
+ return client;
113
105
  }
114
- const subscriptionId = dataReport.subscriptionId as number; // this is checked in the messenger already because we hand over allowed list
115
106
 
116
- const listener = this.subscriptionListeners.get(subscriptionId);
117
- const timer = this.subscriptionUpdateTimers.get(subscriptionId);
107
+ const nodeStore = this.#peers.get(address)?.dataStore;
108
+ await nodeStore?.construction; // Lazy initialize the data if not already done
118
109
 
119
- if (timer !== undefined) {
120
- timer.stop().start(); // Restart timer because we received data
121
- }
110
+ const exchangeProvider = await this.#peers.exchangeProviderFor(address, discoveryOptions);
122
111
 
123
- await listener?.(dataReport);
112
+ client = new InteractionClient(
113
+ exchangeProvider,
114
+ this.#peers.subscriptionClient,
115
+ address,
116
+ this.#peers.interactionQueue,
117
+ nodeStore,
118
+ );
119
+ this.#clients.set(address, client);
120
+
121
+ return client;
124
122
  }
125
123
 
126
- async close() {
127
- this.subscriptionListeners.clear();
128
- this.subscriptionUpdateTimers.forEach(timer => timer.stop());
129
- this.subscriptionUpdateTimers.clear();
124
+ #onPeerLoss(address: PeerAddress) {
125
+ const client = this.#clients.get(address);
126
+ if (client !== undefined) {
127
+ client.close();
128
+ this.#clients.delete(address);
129
+ }
130
130
  }
131
131
  }
132
132
 
133
133
  export class InteractionClient {
134
+ readonly #exchangeProvider: ExchangeProvider;
134
135
  readonly #nodeStore?: PeerDataStore;
135
136
  readonly #ownSubscriptionIds = new Set<number>();
136
137
  readonly #subscriptionClient: SubscriptionClient;
137
138
  readonly #queue?: PromiseQueue;
138
139
 
139
140
  constructor(
140
- private readonly exchangeProvider: ExchangeProvider,
141
+ exchangeProvider: ExchangeProvider,
142
+ subscriptionClient: SubscriptionClient,
141
143
  readonly address: PeerAddress,
142
144
  queue?: PromiseQueue,
143
145
  nodeStore?: PeerDataStore,
144
146
  ) {
147
+ this.#exchangeProvider = exchangeProvider;
145
148
  this.#nodeStore = nodeStore;
149
+ this.#subscriptionClient = subscriptionClient;
146
150
  this.#queue = queue;
147
-
148
- const client = this.exchangeProvider.getProtocolHandler(INTERACTION_PROTOCOL_ID);
149
- if (client === undefined || !(client instanceof SubscriptionClient)) {
150
- throw new InternalError(
151
- `Subscription protocol handler ${INTERACTION_PROTOCOL_ID} missing or unexpected type.`,
152
- );
153
- }
154
- this.#subscriptionClient = client;
155
151
  }
156
152
 
157
153
  get channelUpdated() {
158
- if (this.exchangeProvider instanceof ReconnectableExchangeProvider) {
159
- return this.exchangeProvider.channelUpdated;
154
+ if (this.#exchangeProvider instanceof ReconnectableExchangeProvider) {
155
+ return this.#exchangeProvider.channelUpdated;
160
156
  }
161
157
  throw new ImplementationError("ExchangeProvider does not support channelUpdated");
162
158
  }
163
159
 
164
- registerSubscriptionListener(subscriptionId: number, listener: (dataReport: DataReport) => MaybePromise<void>) {
165
- this.#ownSubscriptionIds.add(subscriptionId);
166
- this.#subscriptionClient.registerSubscriptionListener(subscriptionId, listener);
167
- }
168
-
169
160
  removeSubscription(subscriptionId: number) {
170
161
  this.#ownSubscriptionIds.delete(subscriptionId);
171
- this.#subscriptionClient.removeSubscriptionListener(subscriptionId);
172
- this.#subscriptionClient.removeSubscriptionUpdateTimer(subscriptionId);
162
+ this.#subscriptionClient.delete(subscriptionId);
173
163
  }
174
164
 
175
165
  async getAllAttributes(
@@ -259,10 +249,7 @@ export class InteractionClient {
259
249
  isFabricFiltered?: boolean;
260
250
  executeQueued?: boolean;
261
251
  } = {},
262
- ): Promise<{
263
- attributeReports: DecodedAttributeReportValue<any>[];
264
- eventReports: DecodedEventReportValue<any>[];
265
- }> {
252
+ ): Promise<DecodedDataReport> {
266
253
  const {
267
254
  attributes: attributeRequests,
268
255
  dataVersionFilters,
@@ -302,10 +289,7 @@ export class InteractionClient {
302
289
  );
303
290
  }
304
291
 
305
- const result = await this.withMessenger<{
306
- attributeReports: DecodedAttributeReportValue<any>[];
307
- eventReports: DecodedEventReportValue<any>[];
308
- }>(async messenger => {
292
+ const result = await this.withMessenger(async messenger => {
309
293
  const { isFabricFiltered = true } = options;
310
294
  return await this.processReadRequest(messenger, {
311
295
  attributeRequests,
@@ -404,7 +388,7 @@ export class InteractionClient {
404
388
  private async processReadRequest(
405
389
  messenger: InteractionClientMessenger,
406
390
  request: ReadRequest,
407
- ): Promise<{ attributeReports: DecodedAttributeReportValue<any>[]; eventReports: DecodedEventReportValue<any>[] }> {
391
+ ): Promise<DecodedDataReport> {
408
392
  const { attributeRequests, eventRequests } = request;
409
393
  logger.debug(
410
394
  `Sending read request to ${messenger.getExchangeChannelName()} for attributes ${attributeRequests
@@ -415,10 +399,7 @@ export class InteractionClient {
415
399
  const response = await messenger.sendReadRequest(request);
416
400
 
417
401
  // Normalize and decode the response
418
- const normalizedResult = {
419
- attributeReports: normalizeAndDecodeReadAttributeReport(response.attributeReports ?? []),
420
- eventReports: normalizeAndDecodeReadEventReport(response.eventReports ?? []),
421
- };
402
+ const normalizedResult = DecodedDataReport(response);
422
403
  logger.debug(
423
404
  `Received read response with attributes ${normalizedResult.attributeReports
424
405
  .map(({ path, value }) => `${resolveAttributeName(path)} = ${Logger.toJSON(value)}`)
@@ -643,33 +624,39 @@ export class InteractionClient {
643
624
  return;
644
625
  }
645
626
 
646
- const data = normalizeAndDecodeReadAttributeReport(dataReport.attributeReports);
627
+ const { attributeReports } = DecodedDataReport(dataReport);
647
628
 
648
- if (data.length === 0) {
629
+ if (attributeReports.length === 0) {
649
630
  throw new MatterFlowError("Subscription result reporting undefined/no value not specified");
650
631
  }
651
- if (data.length > 1) {
632
+ if (attributeReports.length > 1) {
652
633
  throw new UnexpectedDataError("Unexpected response with more then one attribute");
653
634
  }
654
- const { value, version } = data[0];
635
+ const { value, version } = attributeReports[0];
655
636
  if (value === undefined)
656
637
  throw new MatterFlowError("Subscription result reporting undefined value not specified.");
657
638
 
658
- await this.#nodeStore?.persistAttributes([data[0]]);
639
+ await this.#nodeStore?.persistAttributes([attributeReports[0]]);
659
640
 
660
641
  listener?.(value, version);
661
642
  };
662
643
 
663
- this.registerSubscriptionListener(subscriptionId, subscriptionListener);
664
- if (updateTimeoutHandler !== undefined) {
665
- this.registerSubscriptionUpdateTimer(
644
+ await this.#registerSubscription(
645
+ {
646
+ id: subscriptionId,
666
647
  maximumPeerResponseTime,
667
- subscriptionId,
668
- maxInterval,
669
- updateTimeoutHandler,
670
- );
671
- }
672
- await subscriptionListener(report);
648
+ maxIntervalS: maxInterval,
649
+ onData: subscriptionListener,
650
+ onTimeout: updateTimeoutHandler,
651
+ },
652
+ report,
653
+ );
654
+ }
655
+
656
+ async #registerSubscription(subscription: RegisteredSubscription, initialReport: DataReport) {
657
+ this.#ownSubscriptionIds.add(subscription.id);
658
+ this.#subscriptionClient.add(subscription);
659
+ await subscription.onData(initialReport);
673
660
  }
674
661
 
675
662
  async subscribeEvent<T, E extends Event<T, any>>(options: {
@@ -736,30 +723,30 @@ export class InteractionClient {
736
723
  return;
737
724
  }
738
725
 
739
- const data = normalizeAndDecodeReadEventReport(dataReport.eventReports);
726
+ const { eventReports } = DecodedDataReport(dataReport);
740
727
 
741
- if (data.length === 0) {
728
+ if (eventReports.length === 0) {
742
729
  throw new MatterFlowError("Received empty subscription result value.");
743
730
  }
744
- if (data.length > 1) {
731
+ if (eventReports.length > 1) {
745
732
  throw new UnexpectedDataError("Unexpected response with more then one attribute.");
746
733
  }
747
- const { events } = data[0];
734
+ const { events } = eventReports[0];
748
735
  if (events === undefined)
749
736
  throw new MatterFlowError("Subscription result reporting undefined value not specified.");
750
737
 
751
738
  events.forEach(event => listener?.(event));
752
739
  };
753
- this.registerSubscriptionListener(subscriptionId, subscriptionListener);
754
- if (updateTimeoutHandler !== undefined) {
755
- this.registerSubscriptionUpdateTimer(
740
+ await this.#registerSubscription(
741
+ {
742
+ id: subscriptionId,
756
743
  maximumPeerResponseTime,
757
- subscriptionId,
758
- maxInterval,
759
- updateTimeoutHandler,
760
- );
761
- }
762
- subscriptionListener(report);
744
+ maxIntervalS: maxInterval,
745
+ onData: subscriptionListener,
746
+ onTimeout: updateTimeoutHandler,
747
+ },
748
+ report,
749
+ );
763
750
  }
764
751
 
765
752
  async subscribeAllAttributesAndEvents(options: {
@@ -957,39 +944,21 @@ export class InteractionClient {
957
944
  }
958
945
  }
959
946
  };
960
- this.registerSubscriptionListener(subscriptionId, async dataReport => {
961
- await subscriptionListener({
962
- ...dataReport,
963
- attributeReports:
964
- dataReport.attributeReports !== undefined
965
- ? normalizeAndDecodeReadAttributeReport(dataReport.attributeReports)
966
- : undefined,
967
- eventReports:
968
- dataReport.eventReports !== undefined
969
- ? normalizeAndDecodeReadEventReport(dataReport.eventReports)
970
- : undefined,
971
- });
972
- });
973
947
 
974
- if (updateTimeoutHandler !== undefined) {
975
- this.registerSubscriptionUpdateTimer(
948
+ const seedReport = DecodedDataReport(report);
949
+
950
+ await this.#registerSubscription(
951
+ {
952
+ id: subscriptionId,
976
953
  maximumPeerResponseTime,
977
- subscriptionId,
978
- maxInterval,
979
- updateTimeoutHandler,
980
- );
981
- }
954
+ maxIntervalS: maxInterval,
982
955
 
983
- const seedReport = {
984
- attributeReports:
985
- report.attributeReports !== undefined
986
- ? normalizeAndDecodeReadAttributeReport(report.attributeReports)
987
- : undefined,
988
- eventReports:
989
- report.eventReports !== undefined ? normalizeAndDecodeReadEventReport(report.eventReports) : undefined,
990
- subscriptionId,
991
- };
992
- await subscriptionListener(seedReport);
956
+ onData: dataReport => subscriptionListener(DecodedDataReport(dataReport)),
957
+
958
+ onTimeout: updateTimeoutHandler,
959
+ },
960
+ seedReport,
961
+ );
993
962
 
994
963
  if (dataVersionFilters !== undefined && dataVersionFilters.length > 0 && enrichCachedAttributeData) {
995
964
  this.#enrichCachedAttributeData(seedReport.attributeReports ?? [], dataVersionFilters);
@@ -1164,7 +1133,7 @@ export class InteractionClient {
1164
1133
  invoke: (messenger: InteractionClientMessenger) => Promise<T>,
1165
1134
  executeQueued = false,
1166
1135
  ): Promise<T> {
1167
- const messenger = await InteractionClientMessenger.create(this.exchangeProvider);
1136
+ const messenger = await InteractionClientMessenger.create(this.#exchangeProvider);
1168
1137
  let result: T;
1169
1138
  try {
1170
1139
  if (executeQueued) {
@@ -1181,27 +1150,6 @@ export class InteractionClient {
1181
1150
  return result;
1182
1151
  }
1183
1152
 
1184
- private registerSubscriptionUpdateTimer(
1185
- maximumPeerResponseTime: number,
1186
- subscriptionId: number,
1187
- maxIntervalS: number,
1188
- updateTimeoutHandler: Timer.Callback,
1189
- ) {
1190
- if (!this.#ownSubscriptionIds.has(subscriptionId)) {
1191
- throw new MatterFlowError(
1192
- `Cannot register update timer for subscription ${subscriptionId} because it is not owned by this client.`,
1193
- );
1194
- }
1195
- const maxIntervalMs = maxIntervalS * 1000 + maximumPeerResponseTime;
1196
-
1197
- const timer = Time.getTimer("Subscription timeout", maxIntervalMs, () => {
1198
- logger.info(`Subscription ${subscriptionId} timed out after ${maxIntervalMs}ms ...`);
1199
- this.removeSubscription(subscriptionId);
1200
- updateTimeoutHandler();
1201
- }).start();
1202
- this.#subscriptionClient.registerSubscriptionUpdateTimer(subscriptionId, timer);
1203
- }
1204
-
1205
1153
  removeAllSubscriptions() {
1206
1154
  for (const subscriptionId of this.#ownSubscriptionIds) {
1207
1155
  this.removeSubscription(subscriptionId);
@@ -1213,11 +1161,11 @@ export class InteractionClient {
1213
1161
  }
1214
1162
 
1215
1163
  get session() {
1216
- return this.exchangeProvider.session;
1164
+ return this.#exchangeProvider.session;
1217
1165
  }
1218
1166
 
1219
1167
  get channelType() {
1220
- return this.exchangeProvider.channelType;
1168
+ return this.#exchangeProvider.channelType;
1221
1169
  }
1222
1170
 
1223
1171
  /** Enrich cached data to get complete responses when data version filters were used. */