@matter/node 0.16.0-alpha.0-20251108-514b3f69e → 0.16.0-alpha.0-20251110-c4c70a41b

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 (212) hide show
  1. package/dist/cjs/behavior/cluster/ClientBehavior.d.ts +0 -7
  2. package/dist/cjs/behavior/cluster/ClientBehavior.d.ts.map +1 -1
  3. package/dist/cjs/behavior/cluster/ClientBehavior.js +2 -8
  4. package/dist/cjs/behavior/cluster/ClientBehavior.js.map +2 -2
  5. package/dist/cjs/behavior/cluster/ClusterBehavior.d.ts +1 -1
  6. package/dist/cjs/behavior/cluster/ClusterBehavior.d.ts.map +1 -1
  7. package/dist/cjs/behavior/cluster/ClusterBehavior.js +9 -4
  8. package/dist/cjs/behavior/cluster/ClusterBehavior.js.map +1 -1
  9. package/dist/cjs/behavior/cluster/ClusterBehaviorCache.d.ts +7 -1
  10. package/dist/cjs/behavior/cluster/ClusterBehaviorCache.d.ts.map +1 -1
  11. package/dist/cjs/behavior/cluster/ClusterBehaviorCache.js +7 -5
  12. package/dist/cjs/behavior/cluster/ClusterBehaviorCache.js.map +1 -1
  13. package/dist/cjs/behavior/cluster/ClusterBehaviorType.d.ts +57 -0
  14. package/dist/cjs/behavior/cluster/ClusterBehaviorType.d.ts.map +1 -0
  15. package/dist/cjs/behavior/cluster/{ClusterBehaviorUtil.js → ClusterBehaviorType.js} +70 -39
  16. package/dist/cjs/behavior/cluster/ClusterBehaviorType.js.map +6 -0
  17. package/dist/cjs/behavior/cluster/ClusterEvents.d.ts +1 -1
  18. package/dist/cjs/behavior/cluster/ClusterEvents.d.ts.map +1 -1
  19. package/dist/cjs/behavior/cluster/ClusterState.d.ts +1 -1
  20. package/dist/cjs/behavior/cluster/ClusterState.d.ts.map +1 -1
  21. package/dist/cjs/behavior/cluster/ValidatedElements.js +2 -2
  22. package/dist/cjs/behavior/cluster/ValidatedElements.js.map +1 -1
  23. package/dist/cjs/behavior/cluster/{ClusterBehaviorUtil.d.ts → cluster-behavior-utils.d.ts} +10 -8
  24. package/dist/cjs/behavior/cluster/cluster-behavior-utils.d.ts.map +1 -0
  25. package/dist/cjs/behavior/cluster/cluster-behavior-utils.js +41 -0
  26. package/dist/cjs/behavior/cluster/cluster-behavior-utils.js.map +6 -0
  27. package/dist/cjs/behavior/cluster/index.d.ts +2 -1
  28. package/dist/cjs/behavior/cluster/index.d.ts.map +1 -1
  29. package/dist/cjs/behavior/cluster/index.js +2 -1
  30. package/dist/cjs/behavior/cluster/index.js.map +1 -1
  31. package/dist/cjs/behavior/context/server/LocalActorContext.d.ts +4 -0
  32. package/dist/cjs/behavior/context/server/LocalActorContext.d.ts.map +1 -1
  33. package/dist/cjs/behavior/context/server/LocalActorContext.js +2 -3
  34. package/dist/cjs/behavior/context/server/LocalActorContext.js.map +1 -1
  35. package/dist/cjs/behavior/context/server/RemoteActorContext.d.ts +4 -0
  36. package/dist/cjs/behavior/context/server/RemoteActorContext.d.ts.map +1 -1
  37. package/dist/cjs/behavior/context/server/RemoteActorContext.js +1 -0
  38. package/dist/cjs/behavior/context/server/RemoteActorContext.js.map +1 -1
  39. package/dist/cjs/behavior/internal/BehaviorBacking.js +2 -2
  40. package/dist/cjs/behavior/internal/BehaviorBacking.js.map +1 -1
  41. package/dist/cjs/behavior/system/commissioning/CommissioningServer.d.ts.map +1 -1
  42. package/dist/cjs/behavior/system/commissioning/CommissioningServer.js +3 -3
  43. package/dist/cjs/behavior/system/commissioning/CommissioningServer.js.map +1 -1
  44. package/dist/cjs/behaviors/operational-credentials/OperationalCredentialsServer.js +1 -1
  45. package/dist/cjs/behaviors/operational-credentials/OperationalCredentialsServer.js.map +1 -1
  46. package/dist/cjs/behaviors/operational-credentials/VendorIdVerification.d.ts +6 -1
  47. package/dist/cjs/behaviors/operational-credentials/VendorIdVerification.d.ts.map +1 -1
  48. package/dist/cjs/behaviors/operational-credentials/VendorIdVerification.js +2 -2
  49. package/dist/cjs/behaviors/operational-credentials/VendorIdVerification.js.map +1 -1
  50. package/dist/cjs/endpoint/Endpoint.d.ts +8 -0
  51. package/dist/cjs/endpoint/Endpoint.d.ts.map +1 -1
  52. package/dist/cjs/endpoint/Endpoint.js +11 -3
  53. package/dist/cjs/endpoint/Endpoint.js.map +1 -1
  54. package/dist/cjs/endpoint/properties/Behaviors.d.ts +1 -1
  55. package/dist/cjs/endpoint/properties/Behaviors.d.ts.map +1 -1
  56. package/dist/cjs/endpoint/properties/Behaviors.js +6 -3
  57. package/dist/cjs/endpoint/properties/Behaviors.js.map +1 -1
  58. package/dist/cjs/endpoint/properties/EndpointInitializer.d.ts +0 -4
  59. package/dist/cjs/endpoint/properties/EndpointInitializer.d.ts.map +1 -1
  60. package/dist/cjs/endpoint/properties/EndpointInitializer.js +0 -6
  61. package/dist/cjs/endpoint/properties/EndpointInitializer.js.map +1 -1
  62. package/dist/cjs/node/Node.d.ts.map +1 -1
  63. package/dist/cjs/node/Node.js +2 -1
  64. package/dist/cjs/node/Node.js.map +1 -1
  65. package/dist/cjs/node/client/ClientCommandMethod.d.ts +11 -0
  66. package/dist/cjs/node/client/ClientCommandMethod.d.ts.map +1 -0
  67. package/dist/cjs/node/client/ClientCommandMethod.js +69 -0
  68. package/dist/cjs/node/client/ClientCommandMethod.js.map +6 -0
  69. package/dist/cjs/node/client/ClientEndpointInitializer.d.ts +0 -2
  70. package/dist/cjs/node/client/ClientEndpointInitializer.d.ts.map +1 -1
  71. package/dist/cjs/node/client/ClientEndpointInitializer.js +3 -34
  72. package/dist/cjs/node/client/ClientEndpointInitializer.js.map +1 -1
  73. package/dist/cjs/node/client/ClientStructure.d.ts.map +1 -1
  74. package/dist/cjs/node/client/ClientStructure.js +164 -32
  75. package/dist/cjs/node/client/ClientStructure.js.map +2 -2
  76. package/dist/cjs/node/client/ClientStructureEvents.d.ts +22 -0
  77. package/dist/cjs/node/client/ClientStructureEvents.d.ts.map +1 -0
  78. package/dist/cjs/node/client/ClientStructureEvents.js +92 -0
  79. package/dist/cjs/node/client/ClientStructureEvents.js.map +6 -0
  80. package/dist/cjs/node/client/PeerBehavior.d.ts +14 -1
  81. package/dist/cjs/node/client/PeerBehavior.d.ts.map +1 -1
  82. package/dist/cjs/node/client/PeerBehavior.js +111 -71
  83. package/dist/cjs/node/client/PeerBehavior.js.map +1 -1
  84. package/dist/cjs/node/client/Peers.d.ts +17 -0
  85. package/dist/cjs/node/client/Peers.d.ts.map +1 -1
  86. package/dist/cjs/node/client/Peers.js +96 -42
  87. package/dist/cjs/node/client/Peers.js.map +1 -1
  88. package/dist/cjs/node/server/ServerSubscription.d.ts +1 -1
  89. package/dist/cjs/node/server/ServerSubscription.d.ts.map +1 -1
  90. package/dist/cjs/node/server/ServerSubscription.js +2 -3
  91. package/dist/cjs/node/server/ServerSubscription.js.map +1 -1
  92. package/dist/esm/behavior/cluster/ClientBehavior.d.ts +0 -7
  93. package/dist/esm/behavior/cluster/ClientBehavior.d.ts.map +1 -1
  94. package/dist/esm/behavior/cluster/ClientBehavior.js +2 -8
  95. package/dist/esm/behavior/cluster/ClientBehavior.js.map +2 -2
  96. package/dist/esm/behavior/cluster/ClusterBehavior.d.ts +1 -1
  97. package/dist/esm/behavior/cluster/ClusterBehavior.d.ts.map +1 -1
  98. package/dist/esm/behavior/cluster/ClusterBehavior.js +9 -4
  99. package/dist/esm/behavior/cluster/ClusterBehavior.js.map +1 -1
  100. package/dist/esm/behavior/cluster/ClusterBehaviorCache.d.ts +7 -1
  101. package/dist/esm/behavior/cluster/ClusterBehaviorCache.d.ts.map +1 -1
  102. package/dist/esm/behavior/cluster/ClusterBehaviorCache.js +7 -5
  103. package/dist/esm/behavior/cluster/ClusterBehaviorCache.js.map +1 -1
  104. package/dist/esm/behavior/cluster/ClusterBehaviorType.d.ts +57 -0
  105. package/dist/esm/behavior/cluster/ClusterBehaviorType.d.ts.map +1 -0
  106. package/dist/esm/behavior/cluster/{ClusterBehaviorUtil.js → ClusterBehaviorType.js} +66 -35
  107. package/dist/esm/behavior/cluster/ClusterBehaviorType.js.map +6 -0
  108. package/dist/esm/behavior/cluster/ClusterEvents.d.ts +1 -1
  109. package/dist/esm/behavior/cluster/ClusterEvents.d.ts.map +1 -1
  110. package/dist/esm/behavior/cluster/ClusterState.d.ts +1 -1
  111. package/dist/esm/behavior/cluster/ClusterState.d.ts.map +1 -1
  112. package/dist/esm/behavior/cluster/ValidatedElements.js +1 -1
  113. package/dist/esm/behavior/cluster/{ClusterBehaviorUtil.d.ts → cluster-behavior-utils.d.ts} +10 -8
  114. package/dist/esm/behavior/cluster/cluster-behavior-utils.d.ts.map +1 -0
  115. package/dist/esm/behavior/cluster/cluster-behavior-utils.js +21 -0
  116. package/dist/esm/behavior/cluster/cluster-behavior-utils.js.map +6 -0
  117. package/dist/esm/behavior/cluster/index.d.ts +2 -1
  118. package/dist/esm/behavior/cluster/index.d.ts.map +1 -1
  119. package/dist/esm/behavior/cluster/index.js +2 -1
  120. package/dist/esm/behavior/cluster/index.js.map +1 -1
  121. package/dist/esm/behavior/context/server/LocalActorContext.d.ts +4 -0
  122. package/dist/esm/behavior/context/server/LocalActorContext.d.ts.map +1 -1
  123. package/dist/esm/behavior/context/server/LocalActorContext.js +2 -3
  124. package/dist/esm/behavior/context/server/LocalActorContext.js.map +1 -1
  125. package/dist/esm/behavior/context/server/RemoteActorContext.d.ts +4 -0
  126. package/dist/esm/behavior/context/server/RemoteActorContext.d.ts.map +1 -1
  127. package/dist/esm/behavior/context/server/RemoteActorContext.js +1 -0
  128. package/dist/esm/behavior/context/server/RemoteActorContext.js.map +1 -1
  129. package/dist/esm/behavior/internal/BehaviorBacking.js +2 -2
  130. package/dist/esm/behavior/internal/BehaviorBacking.js.map +1 -1
  131. package/dist/esm/behavior/system/commissioning/CommissioningServer.d.ts.map +1 -1
  132. package/dist/esm/behavior/system/commissioning/CommissioningServer.js +4 -3
  133. package/dist/esm/behavior/system/commissioning/CommissioningServer.js.map +1 -1
  134. package/dist/esm/behaviors/operational-credentials/OperationalCredentialsServer.js +1 -1
  135. package/dist/esm/behaviors/operational-credentials/OperationalCredentialsServer.js.map +1 -1
  136. package/dist/esm/behaviors/operational-credentials/VendorIdVerification.d.ts +6 -1
  137. package/dist/esm/behaviors/operational-credentials/VendorIdVerification.d.ts.map +1 -1
  138. package/dist/esm/behaviors/operational-credentials/VendorIdVerification.js +1 -1
  139. package/dist/esm/behaviors/operational-credentials/VendorIdVerification.js.map +1 -1
  140. package/dist/esm/endpoint/Endpoint.d.ts +8 -0
  141. package/dist/esm/endpoint/Endpoint.d.ts.map +1 -1
  142. package/dist/esm/endpoint/Endpoint.js +11 -3
  143. package/dist/esm/endpoint/Endpoint.js.map +1 -1
  144. package/dist/esm/endpoint/properties/Behaviors.d.ts +1 -1
  145. package/dist/esm/endpoint/properties/Behaviors.d.ts.map +1 -1
  146. package/dist/esm/endpoint/properties/Behaviors.js +6 -3
  147. package/dist/esm/endpoint/properties/Behaviors.js.map +1 -1
  148. package/dist/esm/endpoint/properties/EndpointInitializer.d.ts +0 -4
  149. package/dist/esm/endpoint/properties/EndpointInitializer.d.ts.map +1 -1
  150. package/dist/esm/endpoint/properties/EndpointInitializer.js +0 -6
  151. package/dist/esm/endpoint/properties/EndpointInitializer.js.map +1 -1
  152. package/dist/esm/node/Node.d.ts.map +1 -1
  153. package/dist/esm/node/Node.js +2 -1
  154. package/dist/esm/node/Node.js.map +1 -1
  155. package/dist/esm/node/client/ClientCommandMethod.d.ts +11 -0
  156. package/dist/esm/node/client/ClientCommandMethod.d.ts.map +1 -0
  157. package/dist/esm/node/client/ClientCommandMethod.js +49 -0
  158. package/dist/esm/node/client/ClientCommandMethod.js.map +6 -0
  159. package/dist/esm/node/client/ClientEndpointInitializer.d.ts +0 -2
  160. package/dist/esm/node/client/ClientEndpointInitializer.d.ts.map +1 -1
  161. package/dist/esm/node/client/ClientEndpointInitializer.js +3 -34
  162. package/dist/esm/node/client/ClientEndpointInitializer.js.map +1 -1
  163. package/dist/esm/node/client/ClientStructure.d.ts.map +1 -1
  164. package/dist/esm/node/client/ClientStructure.js +164 -32
  165. package/dist/esm/node/client/ClientStructure.js.map +2 -2
  166. package/dist/esm/node/client/ClientStructureEvents.d.ts +22 -0
  167. package/dist/esm/node/client/ClientStructureEvents.d.ts.map +1 -0
  168. package/dist/esm/node/client/ClientStructureEvents.js +72 -0
  169. package/dist/esm/node/client/ClientStructureEvents.js.map +6 -0
  170. package/dist/esm/node/client/PeerBehavior.d.ts +14 -1
  171. package/dist/esm/node/client/PeerBehavior.d.ts.map +1 -1
  172. package/dist/esm/node/client/PeerBehavior.js +119 -74
  173. package/dist/esm/node/client/PeerBehavior.js.map +1 -1
  174. package/dist/esm/node/client/Peers.d.ts +17 -0
  175. package/dist/esm/node/client/Peers.d.ts.map +1 -1
  176. package/dist/esm/node/client/Peers.js +106 -43
  177. package/dist/esm/node/client/Peers.js.map +1 -1
  178. package/dist/esm/node/server/ServerSubscription.d.ts +1 -1
  179. package/dist/esm/node/server/ServerSubscription.d.ts.map +1 -1
  180. package/dist/esm/node/server/ServerSubscription.js +2 -3
  181. package/dist/esm/node/server/ServerSubscription.js.map +1 -1
  182. package/package.json +7 -7
  183. package/src/behavior/cluster/ClientBehavior.ts +2 -16
  184. package/src/behavior/cluster/ClusterBehavior.ts +9 -4
  185. package/src/behavior/cluster/ClusterBehaviorCache.ts +16 -9
  186. package/src/behavior/cluster/{ClusterBehaviorUtil.ts → ClusterBehaviorType.ts} +160 -64
  187. package/src/behavior/cluster/ClusterEvents.ts +1 -1
  188. package/src/behavior/cluster/ClusterState.ts +1 -1
  189. package/src/behavior/cluster/ValidatedElements.ts +1 -1
  190. package/src/behavior/cluster/cluster-behavior-utils.ts +48 -0
  191. package/src/behavior/cluster/index.ts +2 -1
  192. package/src/behavior/context/server/LocalActorContext.ts +8 -4
  193. package/src/behavior/context/server/RemoteActorContext.ts +6 -0
  194. package/src/behavior/internal/BehaviorBacking.ts +2 -2
  195. package/src/behavior/system/commissioning/CommissioningServer.ts +4 -3
  196. package/src/behaviors/operational-credentials/OperationalCredentialsServer.ts +1 -1
  197. package/src/behaviors/operational-credentials/VendorIdVerification.ts +3 -2
  198. package/src/endpoint/Endpoint.ts +23 -3
  199. package/src/endpoint/properties/Behaviors.ts +12 -6
  200. package/src/endpoint/properties/EndpointInitializer.ts +0 -7
  201. package/src/node/Node.ts +2 -1
  202. package/src/node/client/ClientCommandMethod.ts +63 -0
  203. package/src/node/client/ClientEndpointInitializer.ts +6 -39
  204. package/src/node/client/ClientStructure.ts +133 -27
  205. package/src/node/client/ClientStructureEvents.ts +97 -0
  206. package/src/node/client/PeerBehavior.ts +185 -104
  207. package/src/node/client/Peers.ts +131 -51
  208. package/src/node/server/ServerSubscription.ts +3 -3
  209. package/dist/cjs/behavior/cluster/ClusterBehaviorUtil.d.ts.map +0 -1
  210. package/dist/cjs/behavior/cluster/ClusterBehaviorUtil.js.map +0 -6
  211. package/dist/esm/behavior/cluster/ClusterBehaviorUtil.d.ts.map +0 -1
  212. package/dist/esm/behavior/cluster/ClusterBehaviorUtil.js.map +0 -6
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Behavior } from "#behavior/Behavior.js";
8
+ import { ClusterBehavior } from "#behavior/cluster/ClusterBehavior.js";
9
+ import { DescriptorClient } from "#behaviors/descriptor";
10
+ import { Endpoint } from "#endpoint/Endpoint.js";
11
+ import { EndpointType } from "#endpoint/type/EndpointType.js";
12
+ import { Environment, Environmental, Observable } from "#general";
13
+ import { DeviceTypeId } from "#types";
14
+
15
+ /**
16
+ * An environmental service that manages events endpoint and behavior types.
17
+ */
18
+ export class ClientStructureEvents {
19
+ #endpointEvents?: Map<DeviceTypeId, Observable<[Endpoint]>>;
20
+ #clusterEvents?: Map<
21
+ string,
22
+ Array<{
23
+ requestedType: Behavior.Type;
24
+ event: Observable<[Endpoint, Behavior.Type]>;
25
+ }>
26
+ >;
27
+
28
+ static [Environmental.create](env: Environment) {
29
+ const instance = new ClientStructureEvents();
30
+ env.set(ClientStructureEvents, instance);
31
+ return instance;
32
+ }
33
+
34
+ endpointInstalled<T extends EndpointType>(type: T): Observable<[endpoint: Endpoint<T>]> {
35
+ if (this.#endpointEvents === undefined) {
36
+ this.#endpointEvents = new Map();
37
+ }
38
+
39
+ let event = this.#endpointEvents.get(type.deviceType);
40
+ if (event === undefined) {
41
+ this.#endpointEvents.set(type.deviceType, (event = Observable()));
42
+ }
43
+
44
+ return event as Observable<[endpoint: Endpoint<T>]>;
45
+ }
46
+
47
+ clusterInstalled<T extends ClusterBehavior.Type>(type: T): Observable<[endpoint: Endpoint, type: T]> {
48
+ if (this.#clusterEvents === undefined) {
49
+ this.#clusterEvents = new Map();
50
+ }
51
+
52
+ let events = this.#clusterEvents.get(type.id);
53
+ if (events === undefined) {
54
+ this.#clusterEvents.set(type.id, (events = []));
55
+ }
56
+
57
+ for (const { requestedType, event } of events) {
58
+ if (requestedType.supports(type)) {
59
+ return event;
60
+ }
61
+ }
62
+
63
+ const event = Observable();
64
+ events.push({ requestedType: type, event });
65
+
66
+ return event;
67
+ }
68
+
69
+ emitEndpoint(endpoint: Endpoint) {
70
+ if (this.#endpointEvents && endpoint.behaviors.supported.descriptor) {
71
+ const deviceTypes = endpoint.stateOf(DescriptorClient).deviceTypeList;
72
+ for (const dt of deviceTypes) {
73
+ this.#endpointEvents.get(dt.deviceType)?.emit(endpoint);
74
+ }
75
+ }
76
+
77
+ for (const type of Object.values(endpoint.behaviors.supported)) {
78
+ this.emitCluster(endpoint, type);
79
+ }
80
+
81
+ for (const part of endpoint.parts) {
82
+ this.emitEndpoint(part);
83
+ }
84
+ }
85
+
86
+ emitCluster(endpoint: Endpoint, type: Behavior.Type) {
87
+ const events = this.#clusterEvents?.get(type.id);
88
+ if (!events) {
89
+ return;
90
+ }
91
+ for (const { requestedType, event } of events) {
92
+ if (type.supports(requestedType)) {
93
+ event.emit(endpoint, type);
94
+ }
95
+ }
96
+ }
97
+ }
@@ -6,11 +6,19 @@
6
6
 
7
7
  import { Behavior } from "#behavior/Behavior.js";
8
8
  import { ClusterBehavior } from "#behavior/cluster/ClusterBehavior.js";
9
+ import { ClusterBehaviorType } from "#behavior/cluster/ClusterBehaviorType.js";
9
10
  import { camelize, capitalize, InternalError } from "#general";
10
- import { AttributeModel, ClusterModel, CommandModel, Conformance, FeatureBitmap, Matter } from "#model";
11
- import type { ClientNode } from "#node/ClientNode.js";
12
- import { Node } from "#node/Node.js";
13
- import { ClientInteraction, Invoke } from "#protocol";
11
+ import {
12
+ AttributeModel,
13
+ ClusterModel,
14
+ CommandModel,
15
+ Conformance,
16
+ EncodedBitmap,
17
+ EventModel,
18
+ FeatureBitmap,
19
+ Matter,
20
+ type ValueModel,
21
+ } from "#model";
14
22
  import {
15
23
  Attribute,
16
24
  AttributeId,
@@ -20,24 +28,77 @@ import {
20
28
  Command,
21
29
  CommandId,
22
30
  MutableCluster,
23
- Status,
24
- StatusResponseError,
25
31
  TlvAny,
26
32
  TlvNoResponse,
27
33
  } from "#types";
34
+ import { ClientCommandMethod } from "./ClientCommandMethod.js";
28
35
 
29
36
  const BIT_BLOCK_SIZE = Math.log2(Number.MAX_SAFE_INTEGER);
30
37
 
31
- const cache = {} as Record<string, ClusterBehavior.Type>;
38
+ const discoveredCache = {} as Record<string, ClusterBehavior.Type>;
39
+ const knownCache = new WeakMap<ClusterBehavior.Type, ClusterBehavior.Type>();
40
+
41
+ const isPeer = Symbol("is-peer");
32
42
 
33
43
  /**
34
44
  * Obtain a {@link ClusterBehavior.Type} for a remote cluster.
35
45
  */
36
46
  export function PeerBehavior(shape: PeerBehavior.ClusterShape): ClusterBehavior.Type {
37
- const analysis = ShapeAnalysis(shape);
47
+ let type: ClusterBehavior.Type;
48
+
49
+ switch (shape.kind) {
50
+ case "known":
51
+ if (Object.hasOwn(shape.behavior, isPeer)) {
52
+ return shape.behavior;
53
+ }
54
+ type = instrumentKnownShape(shape);
55
+ break;
56
+
57
+ case "discovered":
58
+ type = instrumentDiscoveredShape(shape);
59
+ break;
60
+
61
+ default:
62
+ throw new InternalError(`Unknown cluster shape kind ${(shape as any).kind}`);
63
+ }
64
+
65
+ (type as any)[isPeer] = true;
66
+
67
+ return type;
68
+ }
69
+
70
+ export namespace PeerBehavior {
71
+ export type ClusterShape = DiscoveredClusterShape | KnownClusterShape;
72
+
73
+ /**
74
+ * A cluster shape that we assemble using a combination of Matter standards and metadata discovered by reading from
75
+ * a peer.
76
+ */
77
+ export interface DiscoveredClusterShape {
78
+ kind: "discovered";
79
+ id: ClusterId;
80
+ revision: number;
81
+ features: FeatureBitmap | number;
82
+ attributes: AttributeId[];
83
+ commands: CommandId[];
84
+ attributeNames: Record<AttributeId, string>;
85
+ commandNames: Record<CommandId, string>;
86
+ }
87
+
88
+ /**
89
+ * A known cluster shape that we instrument as is.
90
+ */
91
+ export interface KnownClusterShape {
92
+ kind: "known";
93
+ behavior: ClusterBehavior.Type;
94
+ }
95
+ }
96
+
97
+ function instrumentDiscoveredShape(shape: PeerBehavior.DiscoveredClusterShape) {
98
+ const analysis = DiscoveredShapeAnalysis(shape);
38
99
 
39
100
  const fingerprint = createFingerprint(analysis);
40
- let type = cache[fingerprint];
101
+ let type = discoveredCache[fingerprint];
41
102
  if (type) {
42
103
  return type;
43
104
  }
@@ -50,31 +111,41 @@ export function PeerBehavior(shape: PeerBehavior.ClusterShape): ClusterBehavior.
50
111
  baseType = ClusterBehavior;
51
112
  }
52
113
 
53
- type = cache[fingerprint] = generateType(analysis, baseType);
114
+ type = discoveredCache[fingerprint] = generateDiscoveredType(analysis, baseType);
54
115
 
55
116
  return type;
56
117
  }
57
118
 
58
- export namespace PeerBehavior {
59
- export interface ClusterShape {
60
- id: ClusterId;
61
- revision: number;
62
- features: FeatureBitmap | number;
63
- attributes: AttributeId[];
64
- commands: CommandId[];
65
- attributeNames: Record<AttributeId, string>;
66
- commandNames: Record<CommandId, string>;
119
+ function instrumentKnownShape(shape: PeerBehavior.KnownClusterShape) {
120
+ let type = knownCache.get(shape.behavior);
121
+ if (type) {
122
+ return type;
67
123
  }
124
+
125
+ const base = shape.behavior;
126
+
127
+ type = ClusterBehaviorType({
128
+ base,
129
+ cluster: base.cluster,
130
+ schema: base.schema,
131
+ name: `${base.schema.name}Client`,
132
+ forClient: true,
133
+ commandFactory: ClientCommandMethod,
134
+ });
135
+
136
+ knownCache.set(shape.behavior, type);
137
+
138
+ return type;
68
139
  }
69
140
 
70
- function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): ClusterBehavior.Type {
141
+ function generateDiscoveredType(analysis: DiscoveredShapeAnalysis, baseType: Behavior.Type): ClusterBehavior.Type {
71
142
  // Ensure the input type is a ClusterBehavior
72
143
  if (!ClusterBehavior.is(baseType)) {
73
- throw new InternalError(`Base Behavior for cluster ${analysis.schema.name} is not a ClusterBehavior`);
144
+ throw new InternalError(`Base for cluster ${analysis.schema.name} is not a ClusterBehavior`);
74
145
  }
75
146
 
76
147
  let { schema } = analysis;
77
- let isCloned = false;
148
+ let isExtended = false;
78
149
  const { attrSupportOverrides, extraAttrs, commandSupportOverrides, extraCommands } = analysis;
79
150
 
80
151
  // Obtain a ClusterType. This provides TLV for known elements
@@ -99,7 +170,7 @@ function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): Cluster
99
170
  .map(([k]) => k);
100
171
  if (featureNames.length) {
101
172
  // Update ClusterModel
102
- cloneSchema();
173
+ extendSchema();
103
174
 
104
175
  // Update the cluster. Note that we do not validate feature combinations. What the device sends we work with
105
176
  cluster = new ClusterComposer(cluster, true).compose(featureNames.map(capitalize));
@@ -111,10 +182,10 @@ function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): Cluster
111
182
  schema.revision !== analysis.shape.revision ||
112
183
  extraAttrs.size ||
113
184
  extraCommands.size ||
114
- Object.keys(attrSupportOverrides).length ||
115
- Object.keys(commandSupportOverrides).length
185
+ attrSupportOverrides.size ||
186
+ commandSupportOverrides.size
116
187
  ) {
117
- cloneSchema();
188
+ extendSchema();
118
189
 
119
190
  cluster = {
120
191
  ...cluster,
@@ -123,7 +194,7 @@ function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): Cluster
123
194
  commands: { ...cluster.commands },
124
195
  };
125
196
 
126
- if (attrSupportOverrides) {
197
+ if (attrSupportOverrides.size) {
127
198
  for (const [attr, isSupported] of attrSupportOverrides.entries()) {
128
199
  schema.children.push(attr.extend({ operationalIsSupported: isSupported }));
129
200
  }
@@ -135,7 +206,7 @@ function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): Cluster
135
206
  schema.children.push(new AttributeModel({ id, name, type: "any" }));
136
207
  }
137
208
 
138
- if (commandSupportOverrides) {
209
+ if (commandSupportOverrides.size) {
139
210
  for (const [command, isSupported] of commandSupportOverrides.entries()) {
140
211
  schema.children.push(command.extend({ operationalIsSupported: isSupported }));
141
212
  }
@@ -149,72 +220,47 @@ function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): Cluster
149
220
  }
150
221
 
151
222
  // Specialize for the specific cluster and schema
152
- const type = baseType.for(cluster, schema, `${schema.name}Client`);
153
-
154
- // Add command implementations
155
- for (const id of analysis.shape.commands) {
156
- const name = schema.get(CommandModel, id)?.name ?? createUnknownName("command", id);
157
- type.prototype[camelize(name, false)] = implementCommand(camelize(name));
158
- }
159
-
160
- return type;
161
-
162
- function cloneSchema() {
163
- if (isCloned) {
223
+ return ClusterBehaviorType({
224
+ base: baseType,
225
+ cluster,
226
+ schema,
227
+ name: `${schema.name}Client`,
228
+ forClient: true,
229
+ commandFactory: ClientCommandMethod,
230
+ });
231
+
232
+ function extendSchema() {
233
+ if (isExtended) {
164
234
  return;
165
235
  }
166
- schema = schema.clone();
236
+ schema = schema.extend();
167
237
  schema.supportedFeatures = featureNames;
168
- isCloned = true;
169
- }
170
-
171
- function implementCommand(command: string) {
172
- return async function (this: ClusterBehavior, fields?: {}) {
173
- const node = this.env.get(Node) as ClientNode;
174
-
175
- // TODO when implementing TCP add needed logic for Large messages
176
- const chunks = (node.interaction as ClientInteraction).invoke(
177
- Invoke({
178
- commands: [
179
- Invoke.ConcreteCommandRequest<any>({
180
- endpoint: this.endpoint,
181
- cluster,
182
- command,
183
- fields,
184
- }),
185
- ],
186
- }),
187
- );
188
-
189
- for await (const chunk of chunks) {
190
- for (const entry of chunk) {
191
- // We send only one command, so we only get one response back
192
- switch (entry.kind) {
193
- case "cmd-status":
194
- if (entry.status !== Status.Success) {
195
- throw StatusResponseError.create(entry.status, undefined, entry.clusterStatus);
196
- }
197
- return;
198
-
199
- case "cmd-response":
200
- return entry.data;
201
- }
202
- }
203
- }
204
- };
238
+ isExtended = true;
205
239
  }
206
240
  }
207
241
 
208
242
  /**
209
243
  * Create a compact string that uniquely identifies a shape for matching purposes.
210
244
  */
211
- function createFingerprint(analysis: ShapeAnalysis) {
212
- const fingerprint = [analysis.shape.id] as (number | string)[];
245
+ function createFingerprint(analysis: DiscoveredShapeAnalysis) {
246
+ const fingerprint = [analysis.shape.id] as (number | string | bigint)[];
247
+
248
+ if (analysis.featureBitmap) {
249
+ fingerprint.push("f", analysis.featureBitmap);
250
+ }
251
+
252
+ if (analysis.attrSupportOverrides.size) {
253
+ addSupportFingerprints("a", analysis.attrSupportOverrides);
254
+ }
213
255
 
214
256
  if (analysis.extraAttrs.size) {
215
257
  fingerprint.push("a", createElementFingerprint(analysis.extraAttrs));
216
258
  }
217
259
 
260
+ if (analysis.commandSupportOverrides.size) {
261
+ addSupportFingerprints("c", analysis.commandSupportOverrides);
262
+ }
263
+
218
264
  if (analysis.extraCommands.size) {
219
265
  fingerprint.push("c", createElementFingerprint(analysis.extraCommands));
220
266
  }
@@ -224,8 +270,8 @@ function createFingerprint(analysis: ShapeAnalysis) {
224
270
  /**
225
271
  * Create a fingerprint for a specific type of ACE element.
226
272
  *
227
- * For elements we divide create series of bitmaps, one for each range of BIT_BLOCK_SIZE unique integers with an
228
- * ID present. Note that these bitmaps may not be consecutive due to gaps in IDs. We then serialize all bitmaps
273
+ * For elements we create a series of bitmaps, one for each range of BIT_BLOCK_SIZE unique integers with an ID
274
+ * present. Note that these bitmaps may not be consecutive due to gaps in IDs. We then serialize all bitmaps
229
275
  * present as "<block index>:<bitmap value>" and concatenate to create a unique fingerprint.
230
276
  *
231
277
  * The goal is to efficiently create a compact unique identifier.
@@ -243,15 +289,53 @@ function createFingerprint(analysis: ShapeAnalysis) {
243
289
  .map(([block, map]) => (block ? `${block}:${map}` : map))
244
290
  .join(",");
245
291
  }
292
+
293
+ /**
294
+ * Add fingerprints for overrides of element support.
295
+ *
296
+ * This adds an "x-" component for unsupported elements and "x+" for supported elements.
297
+ */
298
+ function addSupportFingerprints(prefix: string, elements: Map<ValueModel, boolean>) {
299
+ let supported: Array<number> | undefined;
300
+ let unsupported: Array<number> | undefined;
301
+
302
+ for (const [{ id }, isSupported] of elements) {
303
+ if (id === undefined) {
304
+ continue;
305
+ }
306
+
307
+ if (isSupported) {
308
+ if (supported) {
309
+ supported.push(id);
310
+ } else {
311
+ supported = [id];
312
+ }
313
+ } else {
314
+ if (unsupported) {
315
+ unsupported.push(id);
316
+ } else {
317
+ unsupported = [id];
318
+ }
319
+ }
320
+ }
321
+
322
+ if (supported) {
323
+ fingerprint.push(`${prefix}+`, createElementFingerprint(supported));
324
+ }
325
+ if (unsupported) {
326
+ fingerprint.push(`${prefix}-`, createElementFingerprint(unsupported));
327
+ }
328
+ }
246
329
  }
247
330
 
248
331
  function createUnknownName(prefix: string, id: number) {
249
332
  return `${prefix}$${id.toString(16)}`;
250
333
  }
251
334
 
252
- interface ShapeAnalysis {
335
+ interface DiscoveredShapeAnalysis {
253
336
  schema: ClusterModel & { id: ClusterId };
254
- shape: PeerBehavior.ClusterShape;
337
+ featureBitmap: number | bigint;
338
+ shape: PeerBehavior.DiscoveredClusterShape;
255
339
  attrSupportOverrides: Map<AttributeModel, boolean>;
256
340
  extraAttrs: Set<number>;
257
341
  commandSupportOverrides: Map<CommandModel, boolean>;
@@ -259,31 +343,25 @@ interface ShapeAnalysis {
259
343
  }
260
344
 
261
345
  /**
262
- * Analyze the cluster shape to determine how we should override the behavior and schema.
346
+ * Analyze a discovered cluster shape to determine how we should override the behavior and schema.
263
347
  */
264
- function ShapeAnalysis(shape: PeerBehavior.ClusterShape): ShapeAnalysis {
348
+ function DiscoveredShapeAnalysis(shape: PeerBehavior.DiscoveredClusterShape): DiscoveredShapeAnalysis {
265
349
  const standardCluster = Matter.get(ClusterModel, shape.id);
266
350
  const schema =
267
351
  standardCluster ??
268
352
  new ClusterModel({ id: shape.id, name: createUnknownName("Cluster", shape.id), revision: shape.revision });
269
353
 
354
+ let featureBitmap: bigint | number;
355
+ if (typeof shape.features === "number") {
356
+ featureBitmap = shape.features;
357
+ } else {
358
+ featureBitmap = EncodedBitmap(schema.featureMap, shape.features);
359
+ }
360
+
270
361
  const attrSupportOverrides = new Map<AttributeModel, boolean>();
271
362
  const extraAttrs = new Set<number>(shape.attributes);
272
363
  for (const attr of schema.attributes) {
273
364
  maybeOverrideSupport(standardCluster, attr, extraAttrs, attrSupportOverrides);
274
- if (standardCluster) {
275
- const supported = extraAttrs.has(attr.id);
276
- const applicability = attr.conformance.applicabilityFor(standardCluster);
277
- if (!supported) {
278
- if (applicability) {
279
- attrSupportOverrides.set(attr, false);
280
- }
281
- } else {
282
- if (applicability !== Conformance.Applicability.Mandatory) {
283
- attrSupportOverrides.set(attr, true);
284
- }
285
- }
286
- }
287
365
  extraAttrs.delete(attr.id as AttributeId);
288
366
  }
289
367
 
@@ -296,6 +374,7 @@ function ShapeAnalysis(shape: PeerBehavior.ClusterShape): ShapeAnalysis {
296
374
 
297
375
  return {
298
376
  schema: schema as ClusterModel & { id: ClusterId },
377
+ featureBitmap,
299
378
  shape,
300
379
  attrSupportOverrides,
301
380
  extraAttrs,
@@ -313,24 +392,26 @@ function ShapeAnalysis(shape: PeerBehavior.ClusterShape): ShapeAnalysis {
313
392
  *
314
393
  * * the element is optional according to the standard and is implemented on the peer
315
394
  */
316
- function maybeOverrideSupport<T extends AttributeModel | CommandModel>(
395
+ function maybeOverrideSupport<T extends AttributeModel | CommandModel | EventModel>(
317
396
  standardCluster: ClusterModel | undefined,
318
397
  element: T,
319
- supported: Set<number>,
398
+ supported: Set<number> | true,
320
399
  overrides: Map<T, boolean>,
321
400
  ) {
322
401
  if (!standardCluster) {
323
402
  return;
324
403
  }
325
404
 
326
- const isSupported = supported.has(element.id);
327
- const applicability = element.conformance.applicabilityFor(standardCluster);
405
+ const isSupported = supported === true || supported.has(element.id);
406
+ const applicability = element.effectiveConformance.applicabilityFor(standardCluster);
328
407
  if (!isSupported) {
329
- if (applicability) {
408
+ if (applicability === Conformance.Applicability.Mandatory) {
409
+ // We don't really pay attention to "unsupported mandatory attributes" but mark them anyway
330
410
  overrides.set(element, false);
331
411
  }
332
412
  } else {
333
413
  if (applicability !== Conformance.Applicability.Mandatory) {
414
+ // Indicate support for optional feature
334
415
  overrides.set(element, true);
335
416
  }
336
417
  }