@matter/node 0.16.0-alpha.0-20251210-206ca2db7 → 0.16.0-alpha.0-20251212-4dde71be3
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.
- package/dist/cjs/behavior/system/network/NetworkClient.d.ts +5 -1
- package/dist/cjs/behavior/system/network/NetworkClient.d.ts.map +1 -1
- package/dist/cjs/behavior/system/network/NetworkClient.js +19 -0
- package/dist/cjs/behavior/system/network/NetworkClient.js.map +1 -1
- package/dist/cjs/endpoint/properties/Behaviors.d.ts.map +1 -1
- package/dist/cjs/endpoint/properties/Behaviors.js +4 -3
- package/dist/cjs/endpoint/properties/Behaviors.js.map +1 -1
- package/dist/cjs/node/client/ClientEventEmitter.d.ts +2 -3
- package/dist/cjs/node/client/ClientEventEmitter.d.ts.map +1 -1
- package/dist/cjs/node/client/ClientEventEmitter.js +10 -2
- package/dist/cjs/node/client/ClientEventEmitter.js.map +1 -1
- package/dist/cjs/node/client/ClientGroupInteraction.d.ts +2 -2
- package/dist/cjs/node/client/ClientGroupInteraction.d.ts.map +1 -1
- package/dist/cjs/node/client/ClientGroupInteraction.js +1 -0
- package/dist/cjs/node/client/ClientGroupInteraction.js.map +1 -1
- package/dist/cjs/node/client/ClientNodeInteraction.js +1 -1
- package/dist/cjs/node/client/ClientNodeInteraction.js.map +1 -1
- package/dist/cjs/node/client/ClientStructure.d.ts +3 -1
- package/dist/cjs/node/client/ClientStructure.d.ts.map +1 -1
- package/dist/cjs/node/client/ClientStructure.js +31 -12
- package/dist/cjs/node/client/ClientStructure.js.map +1 -1
- package/dist/cjs/node/client/NodePeerAddressStore.d.ts.map +1 -1
- package/dist/cjs/node/client/NodePeerAddressStore.js +6 -3
- package/dist/cjs/node/client/NodePeerAddressStore.js.map +1 -1
- package/dist/cjs/node/client/PeerBehavior.d.ts +4 -5
- package/dist/cjs/node/client/PeerBehavior.d.ts.map +1 -1
- package/dist/cjs/node/client/PeerBehavior.js +20 -18
- package/dist/cjs/node/client/PeerBehavior.js.map +1 -1
- package/dist/cjs/node/server/InteractionServer.d.ts.map +1 -1
- package/dist/cjs/node/server/InteractionServer.js.map +1 -1
- package/dist/esm/behavior/system/network/NetworkClient.d.ts +5 -1
- package/dist/esm/behavior/system/network/NetworkClient.d.ts.map +1 -1
- package/dist/esm/behavior/system/network/NetworkClient.js +19 -0
- package/dist/esm/behavior/system/network/NetworkClient.js.map +1 -1
- package/dist/esm/endpoint/properties/Behaviors.d.ts.map +1 -1
- package/dist/esm/endpoint/properties/Behaviors.js +4 -3
- package/dist/esm/endpoint/properties/Behaviors.js.map +1 -1
- package/dist/esm/node/client/ClientEventEmitter.d.ts +2 -3
- package/dist/esm/node/client/ClientEventEmitter.d.ts.map +1 -1
- package/dist/esm/node/client/ClientEventEmitter.js +10 -2
- package/dist/esm/node/client/ClientEventEmitter.js.map +1 -1
- package/dist/esm/node/client/ClientGroupInteraction.d.ts +2 -2
- package/dist/esm/node/client/ClientGroupInteraction.d.ts.map +1 -1
- package/dist/esm/node/client/ClientGroupInteraction.js +1 -0
- package/dist/esm/node/client/ClientGroupInteraction.js.map +1 -1
- package/dist/esm/node/client/ClientNodeInteraction.js +1 -1
- package/dist/esm/node/client/ClientNodeInteraction.js.map +1 -1
- package/dist/esm/node/client/ClientStructure.d.ts +3 -1
- package/dist/esm/node/client/ClientStructure.d.ts.map +1 -1
- package/dist/esm/node/client/ClientStructure.js +32 -12
- package/dist/esm/node/client/ClientStructure.js.map +1 -1
- package/dist/esm/node/client/NodePeerAddressStore.d.ts.map +1 -1
- package/dist/esm/node/client/NodePeerAddressStore.js +6 -3
- package/dist/esm/node/client/NodePeerAddressStore.js.map +1 -1
- package/dist/esm/node/client/PeerBehavior.d.ts +4 -5
- package/dist/esm/node/client/PeerBehavior.d.ts.map +1 -1
- package/dist/esm/node/client/PeerBehavior.js +20 -18
- package/dist/esm/node/client/PeerBehavior.js.map +1 -1
- package/dist/esm/node/server/InteractionServer.d.ts.map +1 -1
- package/dist/esm/node/server/InteractionServer.js.map +1 -1
- package/package.json +7 -7
- package/src/behavior/system/network/NetworkClient.ts +21 -1
- package/src/endpoint/properties/Behaviors.ts +3 -2
- package/src/node/client/ClientEventEmitter.ts +12 -4
- package/src/node/client/ClientGroupInteraction.ts +4 -2
- package/src/node/client/ClientNodeInteraction.ts +1 -1
- package/src/node/client/ClientStructure.ts +37 -15
- package/src/node/client/NodePeerAddressStore.ts +6 -3
- package/src/node/client/PeerBehavior.ts +34 -28
- package/src/node/server/InteractionServer.ts +1 -0
|
@@ -761,9 +761,9 @@ export class Behaviors {
|
|
|
761
761
|
const { id, Events } = type;
|
|
762
762
|
|
|
763
763
|
const get = () => this.#backingFor(type).stateView;
|
|
764
|
-
Object.defineProperty(this.#endpoint.state, id, { get, enumerable: true });
|
|
764
|
+
Object.defineProperty(this.#endpoint.state, id, { get, enumerable: true, configurable: true });
|
|
765
765
|
if (type.schema.id !== undefined) {
|
|
766
|
-
Object.defineProperty(this.#endpoint.state, type.schema.id, { get });
|
|
766
|
+
Object.defineProperty(this.#endpoint.state, type.schema.id, { get, configurable: true });
|
|
767
767
|
}
|
|
768
768
|
|
|
769
769
|
Object.defineProperty(this.#endpoint.events, id, {
|
|
@@ -780,6 +780,7 @@ export class Behaviors {
|
|
|
780
780
|
},
|
|
781
781
|
|
|
782
782
|
enumerable: true,
|
|
783
|
+
configurable: true,
|
|
783
784
|
});
|
|
784
785
|
}
|
|
785
786
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ElementEvent, Events } from "#behavior/Events.js";
|
|
8
|
+
import { NetworkClient } from "#behavior/system/network/NetworkClient.js";
|
|
8
9
|
import { camelize, Diagnostic, isObject, Logger } from "#general";
|
|
9
10
|
import { ClusterModel, EventModel, MatterModel } from "#model";
|
|
10
11
|
import type { ClientNode } from "#node/ClientNode.js";
|
|
@@ -18,10 +19,9 @@ const logger = Logger.get("ClientEventEmitter");
|
|
|
18
19
|
* Event handler for Matter events transmitted by a peer.
|
|
19
20
|
*
|
|
20
21
|
* TODO - set priority on context when split for server vs. client
|
|
21
|
-
* TODO - record latest event number for each subscription shape (or just wildcard?)
|
|
22
22
|
*/
|
|
23
23
|
export interface ClientEventEmitter {
|
|
24
|
-
(event: ReadResult.EventValue): void
|
|
24
|
+
(event: ReadResult.EventValue): Promise<void>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -40,7 +40,7 @@ const warnedForUnknown = new Set<ClusterId | `${ClusterId}-${EventId}`>();
|
|
|
40
40
|
export function ClientEventEmitter(node: ClientNode, structure: ClientStructure) {
|
|
41
41
|
return emitClientEvent;
|
|
42
42
|
|
|
43
|
-
function emitClientEvent(occurrence: ReadResult.EventValue) {
|
|
43
|
+
async function emitClientEvent(occurrence: ReadResult.EventValue) {
|
|
44
44
|
const names = getNames(node.matter, occurrence);
|
|
45
45
|
if (!names) {
|
|
46
46
|
return;
|
|
@@ -48,10 +48,18 @@ export function ClientEventEmitter(node: ClientNode, structure: ClientStructure)
|
|
|
48
48
|
|
|
49
49
|
const event = getEvent(node, occurrence, names.cluster, names.event);
|
|
50
50
|
if (event) {
|
|
51
|
-
node.act(agent => {
|
|
51
|
+
await node.act(async agent => {
|
|
52
52
|
// Current ActionContext is not writable, could skip act() but meh, see TODO above
|
|
53
53
|
//agent.context.priority = occurrence.priority;
|
|
54
54
|
event.emit(occurrence.value, agent.context);
|
|
55
|
+
|
|
56
|
+
const network = agent.get(NetworkClient);
|
|
57
|
+
if (occurrence.number > network.state.maxEventNumber) {
|
|
58
|
+
await agent.context.transaction.addResources(network);
|
|
59
|
+
await agent.context.transaction.begin();
|
|
60
|
+
network.state.maxEventNumber = occurrence.number;
|
|
61
|
+
await agent.context.transaction.commit();
|
|
62
|
+
}
|
|
55
63
|
});
|
|
56
64
|
}
|
|
57
65
|
}
|
|
@@ -8,11 +8,11 @@ import type { ActionContext } from "#behavior/index.js";
|
|
|
8
8
|
import { ImplementationError } from "#general";
|
|
9
9
|
import {
|
|
10
10
|
ClientInvoke,
|
|
11
|
+
ClientSubscription,
|
|
11
12
|
DecodedInvokeResult,
|
|
12
13
|
Read,
|
|
13
14
|
ReadResult,
|
|
14
15
|
Subscribe,
|
|
15
|
-
SubscribeResult,
|
|
16
16
|
Write,
|
|
17
17
|
WriteResult,
|
|
18
18
|
} from "#protocol";
|
|
@@ -27,7 +27,7 @@ export class ClientGroupInteraction extends ClientNodeInteraction {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** Groups do not support reading or subscribing to attributes */
|
|
30
|
-
override async subscribe(_request: Subscribe, _context?: ActionContext):
|
|
30
|
+
override async subscribe(_request: Subscribe, _context?: ActionContext): Promise<ClientSubscription> {
|
|
31
31
|
throw new InvalidGroupOperationError("Groups do not support subscribing to attributes");
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -65,6 +65,8 @@ export class ClientGroupInteraction extends ClientNodeInteraction {
|
|
|
65
65
|
throw new InvalidGroupOperationError("Timed requests are not supported for group address invokes.");
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
request.suppressResponse = true; // Invoking on a group does not yield a response by definition
|
|
69
|
+
|
|
68
70
|
return super.invoke(request, context);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -74,7 +74,7 @@ export class ClientNodeInteraction implements Interactable<ActionContext> {
|
|
|
74
74
|
request,
|
|
75
75
|
}),
|
|
76
76
|
|
|
77
|
-
sustain: request.sustain
|
|
77
|
+
sustain: request.sustain !== false,
|
|
78
78
|
|
|
79
79
|
updated: async data => {
|
|
80
80
|
const result = this.structure.mutate(request, data);
|
|
@@ -11,7 +11,7 @@ import { Descriptor } from "#clusters/descriptor";
|
|
|
11
11
|
import { Endpoint } from "#endpoint/Endpoint.js";
|
|
12
12
|
import { EndpointType } from "#endpoint/type/EndpointType.js";
|
|
13
13
|
import { RootEndpoint } from "#endpoints/root";
|
|
14
|
-
import { Diagnostic, InternalError, isDeepEqual, Logger } from "#general";
|
|
14
|
+
import { Diagnostic, InternalError, isDeepEqual, Logger, Observable } from "#general";
|
|
15
15
|
import {
|
|
16
16
|
AcceptedCommandList,
|
|
17
17
|
AttributeList,
|
|
@@ -19,14 +19,15 @@ import {
|
|
|
19
19
|
DeviceClassification,
|
|
20
20
|
DeviceTypeModel,
|
|
21
21
|
FeatureMap,
|
|
22
|
+
GeneratedCommandList,
|
|
22
23
|
Matter,
|
|
23
24
|
type FeatureBitmap,
|
|
24
25
|
} from "#model";
|
|
25
26
|
import type { ClientNode } from "#node/ClientNode.js";
|
|
26
27
|
import type { Node } from "#node/Node.js";
|
|
27
28
|
import { ReadScope, type Read, type ReadResult } from "#protocol";
|
|
29
|
+
import { ClientNodeStore } from "#storage/client/ClientNodeStore.js";
|
|
28
30
|
import { DatasourceCache } from "#storage/client/DatasourceCache.js";
|
|
29
|
-
import { ClientNodeStore } from "#storage/index.js";
|
|
30
31
|
import type { AttributeId, ClusterId, ClusterType, CommandId, EndpointNumber } from "#types";
|
|
31
32
|
import { Status } from "#types";
|
|
32
33
|
import { ClientEventEmitter } from "./ClientEventEmitter.js";
|
|
@@ -52,6 +53,7 @@ export class ClientStructure {
|
|
|
52
53
|
#pendingStructureEvents = Array<PendingEvent>();
|
|
53
54
|
#delayedClusterEvents = new Array<ReadResult.EventValue>();
|
|
54
55
|
#events: ClientStructureEvents;
|
|
56
|
+
#changed = Observable<[void]>();
|
|
55
57
|
|
|
56
58
|
constructor(node: ClientNode) {
|
|
57
59
|
this.#node = node;
|
|
@@ -64,6 +66,10 @@ export class ClientStructure {
|
|
|
64
66
|
this.#events = this.#node.env.get(ClientStructureEvents);
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
get changed() {
|
|
70
|
+
return this.#changed;
|
|
71
|
+
}
|
|
72
|
+
|
|
67
73
|
/**
|
|
68
74
|
* Load initial structure from cache.
|
|
69
75
|
*/
|
|
@@ -175,14 +181,16 @@ export class ClientStructure {
|
|
|
175
181
|
// Apply changes
|
|
176
182
|
const scope = ReadScope(request);
|
|
177
183
|
for await (const chunk of changes) {
|
|
184
|
+
const chunkData = new Array<ReadResult.Report>();
|
|
178
185
|
for (const change of chunk) {
|
|
186
|
+
chunkData.push(change);
|
|
179
187
|
switch (change.kind) {
|
|
180
188
|
case "attr-value":
|
|
181
189
|
currentUpdates = await this.#mutateAttribute(change, scope, currentUpdates);
|
|
182
190
|
break;
|
|
183
191
|
|
|
184
192
|
case "event-value":
|
|
185
|
-
this.#emitEvent(change, currentUpdates);
|
|
193
|
+
await this.#emitEvent(change, currentUpdates);
|
|
186
194
|
break;
|
|
187
195
|
|
|
188
196
|
case "attr-status":
|
|
@@ -197,7 +205,7 @@ export class ClientStructure {
|
|
|
197
205
|
}
|
|
198
206
|
}
|
|
199
207
|
|
|
200
|
-
yield
|
|
208
|
+
yield chunkData;
|
|
201
209
|
}
|
|
202
210
|
|
|
203
211
|
// The last cluster still needs its changes applied
|
|
@@ -226,6 +234,7 @@ export class ClientStructure {
|
|
|
226
234
|
|
|
227
235
|
// Likewise, we don't emit events until we've applied all structural changes
|
|
228
236
|
this.#emitPendingStructureEvents();
|
|
237
|
+
await this.#emitPendingEvents();
|
|
229
238
|
}
|
|
230
239
|
|
|
231
240
|
/** Reference to the default subscription used when the node was started. */
|
|
@@ -280,7 +289,7 @@ export class ClientStructure {
|
|
|
280
289
|
return currentUpdates;
|
|
281
290
|
}
|
|
282
291
|
|
|
283
|
-
#emitEvent(occurrence: ReadResult.EventValue, currentUpdates?: AttributeUpdates) {
|
|
292
|
+
async #emitEvent(occurrence: ReadResult.EventValue, currentUpdates?: AttributeUpdates) {
|
|
284
293
|
const { endpointId, clusterId } = occurrence.path;
|
|
285
294
|
|
|
286
295
|
const endpoint = this.#endpoints.get(endpointId);
|
|
@@ -291,7 +300,7 @@ export class ClientStructure {
|
|
|
291
300
|
) {
|
|
292
301
|
this.#delayedClusterEvents.push(occurrence);
|
|
293
302
|
} else {
|
|
294
|
-
this.#eventEmitter(occurrence);
|
|
303
|
+
await this.#eventEmitter(occurrence);
|
|
295
304
|
}
|
|
296
305
|
}
|
|
297
306
|
|
|
@@ -370,7 +379,7 @@ export class ClientStructure {
|
|
|
370
379
|
#synchronizeCluster(structure: EndpointStructure, cluster: ClusterStructure) {
|
|
371
380
|
const { endpoint } = structure;
|
|
372
381
|
|
|
373
|
-
// Generate a behavior if enough
|
|
382
|
+
// Generate a behavior if enough information is available
|
|
374
383
|
if (cluster.behavior === undefined) {
|
|
375
384
|
if (cluster.store.initialValues) {
|
|
376
385
|
const {
|
|
@@ -378,6 +387,7 @@ export class ClientStructure {
|
|
|
378
387
|
[FeatureMap.id]: features,
|
|
379
388
|
[AttributeList.id]: attributeList,
|
|
380
389
|
[AcceptedCommandList.id]: commandList,
|
|
390
|
+
[GeneratedCommandList.id]: generatedCommandList,
|
|
381
391
|
} = cluster.store.initialValues;
|
|
382
392
|
|
|
383
393
|
if (typeof clusterRevision === "number") {
|
|
@@ -399,13 +409,21 @@ export class ClientStructure {
|
|
|
399
409
|
(a, b) => a - b,
|
|
400
410
|
);
|
|
401
411
|
}
|
|
412
|
+
|
|
413
|
+
if (Array.isArray(generatedCommandList)) {
|
|
414
|
+
cluster.generatedCommands = (
|
|
415
|
+
generatedCommandList.filter(cmd => typeof cmd === "number") as CommandId[]
|
|
416
|
+
).sort((a, b) => a - b);
|
|
417
|
+
}
|
|
402
418
|
}
|
|
403
419
|
|
|
404
420
|
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
cluster.
|
|
421
|
+
// All global attributes have fallbacks so we can't wait until we're sure we have them all. Instead
|
|
422
|
+
// wait until we are sure there is something useful. We therefore rely on unspecified behavior that all
|
|
423
|
+
// attributes travel consecutively to ensure we initialize fully as we have no other choice
|
|
424
|
+
cluster.attributes?.length ||
|
|
425
|
+
cluster.commands?.length ||
|
|
426
|
+
cluster.generatedCommands?.length
|
|
409
427
|
) {
|
|
410
428
|
const behaviorType = PeerBehavior(cluster as PeerBehavior.ClusterShape);
|
|
411
429
|
|
|
@@ -628,7 +646,7 @@ export class ClientStructure {
|
|
|
628
646
|
/**
|
|
629
647
|
* Replace clusters after activation because fixed global attributes have changed.
|
|
630
648
|
*
|
|
631
|
-
* Currently we apply granular updates to clusters. This will possibly result in subtle errors if peers change in
|
|
649
|
+
* Currently, we apply granular updates to clusters. This will possibly result in subtle errors if peers change in
|
|
632
650
|
* incompatible ways, but the backings are designed to be fairly resilient to this. This is simpler for API users
|
|
633
651
|
* to deal with in the common case where they can just ignore. If it becomes problematic we can revert to replacing
|
|
634
652
|
* entire endpoints or behaviors when there are structural changes.
|
|
@@ -742,8 +760,6 @@ export class ClientStructure {
|
|
|
742
760
|
*/
|
|
743
761
|
#emitPendingStructureEvents() {
|
|
744
762
|
const structureEvents = this.#pendingStructureEvents;
|
|
745
|
-
const clusterEvents = this.#delayedClusterEvents;
|
|
746
|
-
this.#delayedClusterEvents = [];
|
|
747
763
|
this.#pendingStructureEvents = [];
|
|
748
764
|
for (const event of structureEvents) {
|
|
749
765
|
switch (event.kind) {
|
|
@@ -789,8 +805,14 @@ export class ClientStructure {
|
|
|
789
805
|
}
|
|
790
806
|
}
|
|
791
807
|
}
|
|
808
|
+
this.#changed.emit();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
async #emitPendingEvents() {
|
|
812
|
+
const clusterEvents = this.#delayedClusterEvents;
|
|
813
|
+
this.#delayedClusterEvents = [];
|
|
792
814
|
for (const occurrence of clusterEvents) {
|
|
793
|
-
this.#eventEmitter(occurrence);
|
|
815
|
+
await this.#eventEmitter(occurrence);
|
|
794
816
|
}
|
|
795
817
|
}
|
|
796
818
|
}
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { RemoteDescriptor } from "#behavior/system/commissioning/RemoteDescriptor.js";
|
|
8
|
+
import { Crypto, ServerAddress, ServerAddressUdp } from "#general";
|
|
8
9
|
import type { ClientNode } from "#node/ClientNode.js";
|
|
9
10
|
import { IdentityService } from "#node/server/IdentityService.js";
|
|
10
11
|
import type { ServerNode } from "#node/ServerNode.js";
|
|
11
12
|
import { PeerAddress, PeerAddressMap, PeerAddressStore, PeerDescriptor } from "#protocol";
|
|
12
13
|
import { FabricIndex, NodeId } from "#types";
|
|
13
|
-
import { Crypto, ServerAddress, ServerAddressUdp } from "@matter/general";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* This is an adapter for lower-level components in the protocol package.
|
|
@@ -74,17 +74,20 @@ export class NodePeerAddressStore extends PeerAddressStore {
|
|
|
74
74
|
|
|
75
75
|
async updatePeer(peer: PeerDescriptor) {
|
|
76
76
|
const node = this.#owner.peers.get(peer.address);
|
|
77
|
-
if (!node) {
|
|
77
|
+
if (!node || !node.lifecycle.isInstalled) {
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
await node.act(agent => {
|
|
81
|
+
await node.act(async agent => {
|
|
82
|
+
await agent.context.transaction.addResources(agent.commissioning);
|
|
83
|
+
await agent.context.transaction.begin();
|
|
82
84
|
const state = agent.commissioning.state;
|
|
83
85
|
RemoteDescriptor.toLongForm(peer.discoveryData, state);
|
|
84
86
|
if (peer.operationalAddress) {
|
|
85
87
|
// TODO - modify lower tiers to pass along full set of operational addresses
|
|
86
88
|
state.addresses = [peer.operationalAddress];
|
|
87
89
|
}
|
|
90
|
+
await agent.context.transaction.commit();
|
|
88
91
|
});
|
|
89
92
|
}
|
|
90
93
|
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
ClusterComposer,
|
|
26
26
|
ClusterId,
|
|
27
27
|
ClusterRegistry,
|
|
28
|
+
ClusterType,
|
|
28
29
|
Command,
|
|
29
30
|
CommandId,
|
|
30
31
|
MutableCluster,
|
|
@@ -78,11 +79,10 @@ export namespace PeerBehavior {
|
|
|
78
79
|
kind: "discovered";
|
|
79
80
|
id: ClusterId;
|
|
80
81
|
revision: number;
|
|
81
|
-
features
|
|
82
|
-
attributes
|
|
83
|
-
commands
|
|
84
|
-
|
|
85
|
-
commandNames: Record<CommandId, string>;
|
|
82
|
+
features?: FeatureBitmap | number;
|
|
83
|
+
attributes?: AttributeId[];
|
|
84
|
+
commands?: CommandId[];
|
|
85
|
+
generatedCommands?: CommandId[];
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/**
|
|
@@ -103,12 +103,10 @@ function instrumentDiscoveredShape(shape: PeerBehavior.DiscoveredClusterShape) {
|
|
|
103
103
|
return type;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
let baseType: Behavior.Type;
|
|
106
|
+
let baseType: Behavior.Type | undefined;
|
|
107
107
|
const standardCluster = ClusterRegistry.get(shape.id);
|
|
108
|
-
if (standardCluster) {
|
|
108
|
+
if (standardCluster && !standardCluster.name.startsWith("Unknown cluster 0x")) {
|
|
109
109
|
baseType = ClusterBehavior.for(standardCluster);
|
|
110
|
-
} else {
|
|
111
|
-
baseType = ClusterBehavior;
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
type = discoveredCache[fingerprint] = generateDiscoveredType(analysis, baseType);
|
|
@@ -138,30 +136,38 @@ function instrumentKnownShape(shape: PeerBehavior.KnownClusterShape) {
|
|
|
138
136
|
return type;
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
function generateDiscoveredType(analysis: DiscoveredShapeAnalysis, baseType
|
|
142
|
-
// Ensure the input type is a ClusterBehavior
|
|
143
|
-
if (!ClusterBehavior.is(baseType)) {
|
|
144
|
-
throw new InternalError(`Base for cluster ${analysis.schema.name} is not a ClusterBehavior`);
|
|
145
|
-
}
|
|
146
|
-
|
|
139
|
+
function generateDiscoveredType(analysis: DiscoveredShapeAnalysis, baseType?: Behavior.Type): ClusterBehavior.Type {
|
|
147
140
|
let { schema } = analysis;
|
|
148
|
-
let isExtended = false;
|
|
149
|
-
const { attrSupportOverrides, extraAttrs, commandSupportOverrides, extraCommands } = analysis;
|
|
150
141
|
|
|
151
|
-
|
|
152
|
-
let
|
|
153
|
-
|
|
142
|
+
let isExtended: boolean;
|
|
143
|
+
let cluster: ClusterType;
|
|
144
|
+
|
|
145
|
+
if (baseType) {
|
|
146
|
+
isExtended = false;
|
|
147
|
+
|
|
148
|
+
// Ensure the input type is a ClusterBehavior
|
|
149
|
+
if (!ClusterBehavior.is(baseType)) {
|
|
150
|
+
throw new InternalError(`Base for cluster ${analysis.schema.name} is not a ClusterBehavior`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
cluster = baseType.cluster;
|
|
154
|
+
} else {
|
|
155
|
+
isExtended = true;
|
|
156
|
+
|
|
154
157
|
cluster = MutableCluster({ id: schema.id, name: schema.name, revision: schema.revision });
|
|
158
|
+
|
|
159
|
+
baseType = ClusterBehavior;
|
|
155
160
|
}
|
|
156
161
|
|
|
162
|
+
const { attrSupportOverrides, extraAttrs, commandSupportOverrides, extraCommands } = analysis;
|
|
163
|
+
|
|
157
164
|
// Identify known features the device supports
|
|
158
165
|
let supportedFeatures = analysis.shape.features;
|
|
159
166
|
if (typeof supportedFeatures === "number") {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
167
|
+
supportedFeatures = cluster.attributes.featureMap.schema.decode(supportedFeatures as any) as FeatureBitmap;
|
|
168
|
+
}
|
|
169
|
+
if (supportedFeatures === undefined) {
|
|
170
|
+
supportedFeatures = {};
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
// If there are features supported, customize the ClusterModel and ClusterType accordingly
|
|
@@ -354,18 +360,18 @@ function DiscoveredShapeAnalysis(shape: PeerBehavior.DiscoveredClusterShape): Di
|
|
|
354
360
|
if (typeof shape.features === "number") {
|
|
355
361
|
featureBitmap = shape.features;
|
|
356
362
|
} else {
|
|
357
|
-
featureBitmap = EncodedBitmap(schema.featureMap, shape.features);
|
|
363
|
+
featureBitmap = EncodedBitmap(schema.featureMap, shape.features ?? {});
|
|
358
364
|
}
|
|
359
365
|
|
|
360
366
|
const attrSupportOverrides = new Map<AttributeModel, boolean>();
|
|
361
|
-
const extraAttrs = new Set<number>(shape.attributes);
|
|
367
|
+
const extraAttrs = new Set<number>(shape.attributes ?? []);
|
|
362
368
|
for (const attr of schema.attributes) {
|
|
363
369
|
maybeOverrideSupport(standardCluster, attr, extraAttrs, attrSupportOverrides);
|
|
364
370
|
extraAttrs.delete(attr.id as AttributeId);
|
|
365
371
|
}
|
|
366
372
|
|
|
367
373
|
const commandSupportOverrides = new Map<CommandModel, boolean>();
|
|
368
|
-
const extraCommands = new Set(shape.commands);
|
|
374
|
+
const extraCommands = new Set(shape.commands ?? []);
|
|
369
375
|
for (const command of schema.commands) {
|
|
370
376
|
maybeOverrideSupport(standardCluster, command, extraCommands, commandSupportOverrides);
|
|
371
377
|
extraCommands.delete(command.id as CommandId);
|
|
@@ -449,6 +449,7 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
|
|
|
449
449
|
if (fabric !== undefined && !keepSubscriptions) {
|
|
450
450
|
let clearedCount = 0;
|
|
451
451
|
for (const sess of this.#context.sessions.sessions) {
|
|
452
|
+
// TODO Adjust this filtering when subscriptions move to Peer
|
|
452
453
|
if (!PeerAddress.is(sess.peerAddress, session.peerAddress)) {
|
|
453
454
|
// Ignore subscriptions from other peers
|
|
454
455
|
continue;
|