@matter/node 0.16.0-alpha.0-20251016-b56cf5683 → 0.16.0-alpha.0-20251020-3f6e46245
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/Behavior.d.ts +1 -1
- package/dist/cjs/behavior/Behavior.d.ts.map +1 -1
- package/dist/cjs/behavior/Behavior.js +29 -3
- package/dist/cjs/behavior/Behavior.js.map +1 -1
- package/dist/cjs/behavior/cluster/ClusterBehavior.d.ts +1 -1
- package/dist/cjs/behavior/cluster/ClusterBehavior.d.ts.map +1 -1
- package/dist/cjs/behavior/cluster/ClusterBehaviorUtil.d.ts.map +1 -1
- package/dist/cjs/behavior/cluster/ClusterBehaviorUtil.js +2 -4
- package/dist/cjs/behavior/cluster/ClusterBehaviorUtil.js.map +1 -1
- package/dist/cjs/behavior/supervision/RootSupervisor.d.ts.map +1 -1
- package/dist/cjs/behavior/supervision/RootSupervisor.js +2 -1
- package/dist/cjs/behavior/supervision/RootSupervisor.js.map +1 -1
- package/dist/cjs/behavior/system/commissioning/CommissioningClient.d.ts +35 -11
- package/dist/cjs/behavior/system/commissioning/CommissioningClient.d.ts.map +1 -1
- package/dist/cjs/behavior/system/commissioning/CommissioningClient.js +147 -138
- package/dist/cjs/behavior/system/commissioning/CommissioningClient.js.map +2 -2
- package/dist/cjs/behavior/system/commissioning/CommissioningServer.d.ts +0 -7
- package/dist/cjs/behavior/system/commissioning/CommissioningServer.d.ts.map +1 -1
- package/dist/cjs/behavior/system/commissioning/CommissioningServer.js +23 -27
- package/dist/cjs/behavior/system/commissioning/CommissioningServer.js.map +1 -1
- package/dist/cjs/behavior/system/commissioning/RemoteDescriptor.js +4 -4
- package/dist/cjs/behavior/system/commissioning/RemoteDescriptor.js.map +1 -1
- package/dist/cjs/behavior/system/network/NetworkClient.d.ts.map +1 -1
- package/dist/cjs/behavior/system/network/NetworkClient.js +7 -2
- package/dist/cjs/behavior/system/network/NetworkClient.js.map +1 -1
- package/dist/cjs/behavior/system/network/NetworkServer.js +1 -1
- package/dist/cjs/behavior/system/network/NetworkServer.js.map +1 -1
- package/dist/cjs/behaviors/access-control/AccessControlServer.d.ts.map +1 -1
- package/dist/cjs/behaviors/access-control/AccessControlServer.js +2 -9
- package/dist/cjs/behaviors/access-control/AccessControlServer.js.map +1 -1
- package/dist/cjs/behaviors/general-commissioning/GeneralCommissioningServer.js +1 -1
- package/dist/cjs/behaviors/general-commissioning/GeneralCommissioningServer.js.map +1 -1
- package/dist/cjs/node/ClientGroup.d.ts +18 -0
- package/dist/cjs/node/ClientGroup.d.ts.map +1 -0
- package/dist/cjs/node/ClientGroup.js +53 -0
- package/dist/cjs/node/ClientGroup.js.map +6 -0
- package/dist/cjs/node/ClientNode.d.ts +3 -0
- package/dist/cjs/node/ClientNode.d.ts.map +1 -1
- package/dist/cjs/node/ClientNode.js +8 -2
- package/dist/cjs/node/ClientNode.js.map +1 -1
- package/dist/cjs/node/client/ClientBehavior.d.ts.map +1 -1
- package/dist/cjs/node/client/ClientBehavior.js +13 -12
- package/dist/cjs/node/client/ClientBehavior.js.map +1 -1
- package/dist/cjs/node/client/ClientGroupInteraction.d.ts +22 -0
- package/dist/cjs/node/client/ClientGroupInteraction.d.ts.map +1 -0
- package/dist/cjs/node/client/ClientGroupInteraction.js +67 -0
- package/dist/cjs/node/client/ClientGroupInteraction.js.map +6 -0
- package/dist/cjs/node/client/ClientNodeFactory.d.ts +2 -1
- package/dist/cjs/node/client/ClientNodeFactory.d.ts.map +1 -1
- package/dist/cjs/node/client/ClientNodeFactory.js.map +1 -1
- package/dist/cjs/node/client/ClientNodeInteraction.d.ts +16 -2
- package/dist/cjs/node/client/ClientNodeInteraction.d.ts.map +1 -1
- package/dist/cjs/node/client/ClientNodeInteraction.js +14 -0
- package/dist/cjs/node/client/ClientNodeInteraction.js.map +1 -1
- package/dist/cjs/node/client/Peers.d.ts.map +1 -1
- package/dist/cjs/node/client/Peers.js +21 -8
- package/dist/cjs/node/client/Peers.js.map +1 -1
- package/dist/cjs/node/integration/ProtocolService.d.ts.map +1 -1
- package/dist/cjs/node/integration/ProtocolService.js +10 -16
- package/dist/cjs/node/integration/ProtocolService.js.map +1 -1
- package/dist/cjs/node/server/InteractionServer.d.ts.map +1 -1
- package/dist/cjs/node/server/InteractionServer.js +87 -39
- package/dist/cjs/node/server/InteractionServer.js.map +1 -1
- package/dist/cjs/node/server/OnlineServerInteraction.js +1 -1
- package/dist/cjs/node/server/OnlineServerInteraction.js.map +1 -1
- package/dist/cjs/storage/client/ClientNodeStores.d.ts +2 -0
- package/dist/cjs/storage/client/ClientNodeStores.d.ts.map +1 -1
- package/dist/cjs/storage/client/ClientNodeStores.js +19 -0
- package/dist/cjs/storage/client/ClientNodeStores.js.map +1 -1
- package/dist/esm/behavior/Behavior.d.ts +1 -1
- package/dist/esm/behavior/Behavior.d.ts.map +1 -1
- package/dist/esm/behavior/Behavior.js +29 -3
- package/dist/esm/behavior/Behavior.js.map +1 -1
- package/dist/esm/behavior/cluster/ClusterBehavior.d.ts +1 -1
- package/dist/esm/behavior/cluster/ClusterBehavior.d.ts.map +1 -1
- package/dist/esm/behavior/cluster/ClusterBehaviorUtil.d.ts.map +1 -1
- package/dist/esm/behavior/cluster/ClusterBehaviorUtil.js +3 -4
- package/dist/esm/behavior/cluster/ClusterBehaviorUtil.js.map +1 -1
- package/dist/esm/behavior/supervision/RootSupervisor.d.ts.map +1 -1
- package/dist/esm/behavior/supervision/RootSupervisor.js +3 -1
- package/dist/esm/behavior/supervision/RootSupervisor.js.map +1 -1
- package/dist/esm/behavior/system/commissioning/CommissioningClient.d.ts +35 -11
- package/dist/esm/behavior/system/commissioning/CommissioningClient.d.ts.map +1 -1
- package/dist/esm/behavior/system/commissioning/CommissioningClient.js +167 -141
- package/dist/esm/behavior/system/commissioning/CommissioningClient.js.map +2 -2
- package/dist/esm/behavior/system/commissioning/CommissioningServer.d.ts +0 -7
- package/dist/esm/behavior/system/commissioning/CommissioningServer.d.ts.map +1 -1
- package/dist/esm/behavior/system/commissioning/CommissioningServer.js +23 -27
- package/dist/esm/behavior/system/commissioning/CommissioningServer.js.map +1 -1
- package/dist/esm/behavior/system/commissioning/RemoteDescriptor.js +4 -4
- package/dist/esm/behavior/system/commissioning/RemoteDescriptor.js.map +1 -1
- package/dist/esm/behavior/system/network/NetworkClient.d.ts.map +1 -1
- package/dist/esm/behavior/system/network/NetworkClient.js +7 -2
- package/dist/esm/behavior/system/network/NetworkClient.js.map +1 -1
- package/dist/esm/behavior/system/network/NetworkServer.js +1 -1
- package/dist/esm/behavior/system/network/NetworkServer.js.map +1 -1
- package/dist/esm/behaviors/access-control/AccessControlServer.d.ts.map +1 -1
- package/dist/esm/behaviors/access-control/AccessControlServer.js +2 -9
- package/dist/esm/behaviors/access-control/AccessControlServer.js.map +1 -1
- package/dist/esm/behaviors/general-commissioning/GeneralCommissioningServer.js +1 -1
- package/dist/esm/behaviors/general-commissioning/GeneralCommissioningServer.js.map +1 -1
- package/dist/esm/node/ClientGroup.d.ts +18 -0
- package/dist/esm/node/ClientGroup.d.ts.map +1 -0
- package/dist/esm/node/ClientGroup.js +33 -0
- package/dist/esm/node/ClientGroup.js.map +6 -0
- package/dist/esm/node/ClientNode.d.ts +3 -0
- package/dist/esm/node/ClientNode.d.ts.map +1 -1
- package/dist/esm/node/ClientNode.js +8 -2
- package/dist/esm/node/ClientNode.js.map +1 -1
- package/dist/esm/node/client/ClientBehavior.d.ts.map +1 -1
- package/dist/esm/node/client/ClientBehavior.js +13 -12
- package/dist/esm/node/client/ClientBehavior.js.map +1 -1
- package/dist/esm/node/client/ClientGroupInteraction.d.ts +22 -0
- package/dist/esm/node/client/ClientGroupInteraction.d.ts.map +1 -0
- package/dist/esm/node/client/ClientGroupInteraction.js +47 -0
- package/dist/esm/node/client/ClientGroupInteraction.js.map +6 -0
- package/dist/esm/node/client/ClientNodeFactory.d.ts +2 -1
- package/dist/esm/node/client/ClientNodeFactory.d.ts.map +1 -1
- package/dist/esm/node/client/ClientNodeFactory.js.map +1 -1
- package/dist/esm/node/client/ClientNodeInteraction.d.ts +16 -2
- package/dist/esm/node/client/ClientNodeInteraction.d.ts.map +1 -1
- package/dist/esm/node/client/ClientNodeInteraction.js +17 -1
- package/dist/esm/node/client/ClientNodeInteraction.js.map +1 -1
- package/dist/esm/node/client/Peers.d.ts.map +1 -1
- package/dist/esm/node/client/Peers.js +21 -8
- package/dist/esm/node/client/Peers.js.map +1 -1
- package/dist/esm/node/integration/ProtocolService.d.ts.map +1 -1
- package/dist/esm/node/integration/ProtocolService.js +16 -17
- package/dist/esm/node/integration/ProtocolService.js.map +1 -1
- package/dist/esm/node/server/InteractionServer.d.ts.map +1 -1
- package/dist/esm/node/server/InteractionServer.js +88 -39
- package/dist/esm/node/server/InteractionServer.js.map +1 -1
- package/dist/esm/node/server/OnlineServerInteraction.js +1 -1
- package/dist/esm/node/server/OnlineServerInteraction.js.map +1 -1
- package/dist/esm/storage/client/ClientNodeStores.d.ts +2 -0
- package/dist/esm/storage/client/ClientNodeStores.d.ts.map +1 -1
- package/dist/esm/storage/client/ClientNodeStores.js +20 -1
- package/dist/esm/storage/client/ClientNodeStores.js.map +1 -1
- package/package.json +7 -7
- package/src/behavior/Behavior.ts +42 -4
- package/src/behavior/cluster/ClusterBehavior.ts +1 -1
- package/src/behavior/cluster/ClusterBehaviorUtil.ts +7 -7
- package/src/behavior/supervision/RootSupervisor.ts +4 -2
- package/src/behavior/system/commissioning/CommissioningClient.ts +113 -73
- package/src/behavior/system/commissioning/CommissioningServer.ts +25 -30
- package/src/behavior/system/commissioning/RemoteDescriptor.ts +4 -4
- package/src/behavior/system/network/NetworkClient.ts +8 -2
- package/src/behavior/system/network/NetworkServer.ts +1 -1
- package/src/behaviors/access-control/AccessControlServer.ts +1 -8
- package/src/behaviors/general-commissioning/GeneralCommissioningServer.ts +1 -1
- package/src/node/ClientGroup.ts +36 -0
- package/src/node/ClientNode.ts +10 -2
- package/src/node/client/ClientBehavior.ts +17 -17
- package/src/node/client/ClientEndpointInitializer.ts +1 -1
- package/src/node/client/ClientGroupInteraction.ts +70 -0
- package/src/node/client/ClientNodeFactory.ts +2 -1
- package/src/node/client/ClientNodeInteraction.ts +19 -5
- package/src/node/client/Peers.ts +23 -8
- package/src/node/integration/ProtocolService.ts +18 -18
- package/src/node/server/InteractionServer.ts +108 -57
- package/src/node/server/OnlineServerInteraction.ts +1 -1
- package/src/storage/client/ClientNodeStores.ts +25 -1
|
@@ -77,21 +77,14 @@ export class AccessControlServer extends AccessControlBehavior.with("Extension")
|
|
|
77
77
|
// Handle Backward compatibility to Matter.js before 0.9.1 and add the missing ACL entry if no entry was set
|
|
78
78
|
// so far by the controller
|
|
79
79
|
const fallbackAcl: AccessControlTypes.AccessControlEntry = {
|
|
80
|
-
fabricIndex: fabric.fabricIndex,
|
|
81
80
|
privilege: AccessControlTypes.AccessControlEntryPrivilege.Administer,
|
|
82
81
|
authMode: AccessControlTypes.AccessControlEntryAuthMode.Case,
|
|
83
82
|
subjects: [fabric.rootNodeId],
|
|
84
83
|
targets: null, // entire node
|
|
84
|
+
fabricIndex: fabric.fabricIndex,
|
|
85
85
|
};
|
|
86
86
|
this.state.acl.push(fallbackAcl);
|
|
87
87
|
fabricAcls.push(fallbackAcl);
|
|
88
|
-
logger.warn(
|
|
89
|
-
"Added missing ACL entry for fabric",
|
|
90
|
-
fabric.fabricIndex,
|
|
91
|
-
"for Node ID",
|
|
92
|
-
fabric.rootNodeId,
|
|
93
|
-
". This should only happen once after upgrading to matter.js 0.9.1",
|
|
94
|
-
);
|
|
95
88
|
}
|
|
96
89
|
fabric.accessControl.aclList = fabricAcls;
|
|
97
90
|
fabric.accessControl.extensionEntryAccessCheck = this.extensionEntryAccessCheck.bind(this);
|
|
@@ -99,7 +99,7 @@ export class GeneralCommissioningServer extends GeneralCommissioningBehavior {
|
|
|
99
99
|
sessions: this.env.get(SessionManager),
|
|
100
100
|
expiryLength: Seconds(expiryLengthSeconds),
|
|
101
101
|
maxCumulativeFailsafe: Seconds(this.state.basicCommissioningInfo.maxCumulativeFailsafeSeconds),
|
|
102
|
-
|
|
102
|
+
session,
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
// Note - this used to be async and wait for construction internally. However that leads to race
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { ActionContext } from "#behavior/context/ActionContext.js";
|
|
7
|
+
import { Interactable } from "#protocol";
|
|
8
|
+
import { ServerNodeStore } from "#storage/index.js";
|
|
9
|
+
import { ClientNode } from "./ClientNode.js";
|
|
10
|
+
import { ClientGroupInteraction } from "./client/ClientGroupInteraction.js";
|
|
11
|
+
|
|
12
|
+
export class ClientGroup extends ClientNode {
|
|
13
|
+
#interaction?: ClientGroupInteraction;
|
|
14
|
+
|
|
15
|
+
override get isGroup() {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override get interaction(): Interactable<ActionContext> {
|
|
20
|
+
if (this.#interaction === undefined) {
|
|
21
|
+
this.#interaction = new ClientGroupInteraction(this);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return this.#interaction;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected override get store() {
|
|
28
|
+
return this.env.get(ServerNodeStore).clientStores.storeForGroup(this);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export namespace ClientGroup {
|
|
33
|
+
export function is(value: unknown): value is ClientGroup {
|
|
34
|
+
return value instanceof ClientGroup;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/node/ClientNode.ts
CHANGED
|
@@ -53,6 +53,10 @@ export class ClientNode extends Node<ClientNode.RootEndpoint> {
|
|
|
53
53
|
this.#matter = options.matter ?? Matter;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
get isGroup() {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
56
60
|
/**
|
|
57
61
|
* Model of Matter semantics understood by this node.
|
|
58
62
|
*
|
|
@@ -66,8 +70,12 @@ export class ClientNode extends Node<ClientNode.RootEndpoint> {
|
|
|
66
70
|
return new ClientNodeEndpoints(this);
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
protected get store() {
|
|
74
|
+
return this.env.get(ServerNodeStore).clientStores.storeForNode(this);
|
|
75
|
+
}
|
|
76
|
+
|
|
69
77
|
override initialize() {
|
|
70
|
-
const store = this.
|
|
78
|
+
const store = this.store;
|
|
71
79
|
|
|
72
80
|
this.env.set(ClientNodeStore, store);
|
|
73
81
|
|
|
@@ -202,7 +210,7 @@ export class ClientNode extends Node<ClientNode.RootEndpoint> {
|
|
|
202
210
|
|
|
203
211
|
// During early initialization commissioning state may not be loaded, so check directly in storage too
|
|
204
212
|
if (!address) {
|
|
205
|
-
address = this.
|
|
213
|
+
address = this.store.storeForEndpoint(this).peerAddress as PeerAddress | undefined;
|
|
206
214
|
}
|
|
207
215
|
|
|
208
216
|
// Use the peer address as a log identifier if present
|
|
@@ -10,14 +10,13 @@ import { camelize, capitalize, InternalError } from "#general";
|
|
|
10
10
|
import { AttributeModel, ClusterModel, CommandModel, FeatureBitmap, Matter } from "#model";
|
|
11
11
|
import type { ClientNode } from "#node/ClientNode.js";
|
|
12
12
|
import { Node } from "#node/Node.js";
|
|
13
|
-
import { Invoke } from "#protocol";
|
|
13
|
+
import { ClientInteraction, Invoke } from "#protocol";
|
|
14
14
|
import {
|
|
15
15
|
Attribute,
|
|
16
16
|
AttributeId,
|
|
17
17
|
ClusterComposer,
|
|
18
18
|
ClusterId,
|
|
19
19
|
ClusterRegistry,
|
|
20
|
-
ClusterType,
|
|
21
20
|
Command,
|
|
22
21
|
CommandId,
|
|
23
22
|
MutableCluster,
|
|
@@ -137,8 +136,7 @@ function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): Cluster
|
|
|
137
136
|
// Add command implementations
|
|
138
137
|
for (const id of analysis.shape.commands) {
|
|
139
138
|
const name = schema.get(CommandModel, id)?.name ?? createUnknownName("command", id);
|
|
140
|
-
|
|
141
|
-
type.prototype[camelize(name, false)] = implementCommand(command);
|
|
139
|
+
type.prototype[camelize(name, false)] = implementCommand(camelize(name));
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
return type;
|
|
@@ -152,33 +150,35 @@ function generateType(analysis: ShapeAnalysis, baseType: Behavior.Type): Cluster
|
|
|
152
150
|
isCloned = true;
|
|
153
151
|
}
|
|
154
152
|
|
|
155
|
-
function implementCommand(command:
|
|
153
|
+
function implementCommand(command: string) {
|
|
156
154
|
return async function (this: ClusterBehavior, fields?: {}) {
|
|
157
155
|
const node = this.env.get(Node) as ClientNode;
|
|
158
156
|
|
|
159
|
-
const chunks = node.interaction.invoke(
|
|
160
|
-
Invoke(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
157
|
+
const chunks = (node.interaction as ClientInteraction).invoke(
|
|
158
|
+
Invoke({
|
|
159
|
+
commands: [
|
|
160
|
+
Invoke.ConcreteCommandRequest<any>({
|
|
161
|
+
endpoint: this.endpoint,
|
|
162
|
+
cluster,
|
|
163
|
+
command,
|
|
164
|
+
fields,
|
|
165
|
+
}),
|
|
166
|
+
],
|
|
167
|
+
}),
|
|
168
168
|
);
|
|
169
169
|
|
|
170
170
|
for await (const chunk of chunks) {
|
|
171
171
|
for (const entry of chunk) {
|
|
172
|
-
//
|
|
172
|
+
// We send only one command, so we only get one response back
|
|
173
173
|
switch (entry.kind) {
|
|
174
174
|
case "cmd-status":
|
|
175
175
|
if (entry.status !== Status.Success) {
|
|
176
176
|
throw StatusResponseError.create(entry.status, undefined, entry.clusterStatus);
|
|
177
177
|
}
|
|
178
|
-
|
|
178
|
+
return;
|
|
179
179
|
|
|
180
180
|
case "cmd-response":
|
|
181
|
-
return
|
|
181
|
+
return entry.data;
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
}
|
|
@@ -49,7 +49,7 @@ export class ClientEndpointInitializer extends EndpointInitializer {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
override createBacking(endpoint: Endpoint, type: Behavior.Type): BehaviorBacking {
|
|
52
|
-
// Non-cluster behaviors are local
|
|
52
|
+
// Non-cluster behaviors are local, operating the same server behaviors
|
|
53
53
|
if ((type as ClusterBehavior.Type).cluster === undefined) {
|
|
54
54
|
const store = this.structure.storeForLocal(endpoint, type);
|
|
55
55
|
return new ServerBehaviorBacking(endpoint, type, store, endpoint.behaviors.optionsFor(type));
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ActionContext } from "#behavior/index.js";
|
|
8
|
+
import { ImplementationError } from "#general";
|
|
9
|
+
import {
|
|
10
|
+
ClientInvoke,
|
|
11
|
+
DecodedInvokeResult,
|
|
12
|
+
Read,
|
|
13
|
+
ReadResult,
|
|
14
|
+
Subscribe,
|
|
15
|
+
SubscribeResult,
|
|
16
|
+
Write,
|
|
17
|
+
WriteResult,
|
|
18
|
+
} from "#protocol";
|
|
19
|
+
import { ClientNodeInteraction } from "./ClientNodeInteraction.js";
|
|
20
|
+
|
|
21
|
+
export class InvalidGroupOperationError extends ImplementationError {}
|
|
22
|
+
|
|
23
|
+
export class ClientGroupInteraction extends ClientNodeInteraction {
|
|
24
|
+
/** Groups do not support reading or subscribing to attributes */
|
|
25
|
+
override read(_request: Read, _context?: ActionContext): ReadResult {
|
|
26
|
+
throw new InvalidGroupOperationError("Groups do not support reading attributes");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Groups do not support reading or subscribing to attributes */
|
|
30
|
+
override async subscribe(_request: Subscribe, _context?: ActionContext): SubscribeResult {
|
|
31
|
+
throw new InvalidGroupOperationError("Groups do not support subscribing to attributes");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override async write<T extends Write>(
|
|
35
|
+
request: T,
|
|
36
|
+
context?: ActionContext,
|
|
37
|
+
): WriteResult<T & { suppressResponse: true }> {
|
|
38
|
+
if (request.timedRequest) {
|
|
39
|
+
throw new InvalidGroupOperationError("Timed requests are not supported for group address writes.");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (request.suppressResponse === false) {
|
|
43
|
+
// If flag was explicitly set to false, we cannot comply
|
|
44
|
+
throw new InvalidGroupOperationError("Writing attributes on a group address can not return a response.");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
request.writeRequests.some(
|
|
49
|
+
({ path: { endpointId, clusterId, attributeId } }) =>
|
|
50
|
+
endpointId !== undefined || clusterId === undefined || attributeId === undefined,
|
|
51
|
+
)
|
|
52
|
+
) {
|
|
53
|
+
throw new InvalidGroupOperationError("Not all attribute write paths are valid for group address writes.");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Writing to a group does not yield a response
|
|
57
|
+
return super.write({ ...request, suppressResponse: true }, context);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override invoke(request: ClientInvoke, context?: ActionContext): DecodedInvokeResult {
|
|
61
|
+
if (request.invokeRequests.some(({ commandPath: { endpointId } }) => endpointId !== undefined)) {
|
|
62
|
+
throw new InvalidGroupOperationError("Invoking a concrete command on a group address is not supported.");
|
|
63
|
+
}
|
|
64
|
+
if (request.timedRequest) {
|
|
65
|
+
throw new InvalidGroupOperationError("Timed requests are not supported for group address invokes.");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return super.invoke(request, context);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { RemoteDescriptor } from "#behavior/system/commissioning/RemoteDescriptor.js";
|
|
8
8
|
import type { ImmutableSet } from "#general";
|
|
9
9
|
import type { ClientNode } from "#node/ClientNode.js";
|
|
10
|
+
import type { PeerAddress } from "#protocol";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Create a new client node.
|
|
@@ -16,6 +17,6 @@ import type { ClientNode } from "#node/ClientNode.js";
|
|
|
16
17
|
*/
|
|
17
18
|
export abstract class ClientNodeFactory {
|
|
18
19
|
abstract find(descriptor: RemoteDescriptor): ClientNode | undefined;
|
|
19
|
-
abstract create(options: ClientNode.Options): ClientNode;
|
|
20
|
+
abstract create(options: ClientNode.Options, peerAddress?: PeerAddress): ClientNode;
|
|
20
21
|
abstract nodes: ImmutableSet<ClientNode>;
|
|
21
22
|
}
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
import type { ActionContext } from "#behavior/context/ActionContext.js";
|
|
8
8
|
import { EndpointInitializer } from "#endpoint/properties/EndpointInitializer.js";
|
|
9
9
|
import type { ClientNode } from "#node/ClientNode.js";
|
|
10
|
-
import
|
|
10
|
+
import {
|
|
11
|
+
ClientInteraction,
|
|
12
|
+
ClientInvoke,
|
|
13
|
+
DecodedInvokeResult,
|
|
11
14
|
Interactable,
|
|
12
|
-
InvokeRequest,
|
|
13
|
-
InvokeResult,
|
|
14
15
|
Read,
|
|
15
16
|
ReadResult,
|
|
16
17
|
Subscribe,
|
|
@@ -18,7 +19,6 @@ import type {
|
|
|
18
19
|
Write,
|
|
19
20
|
WriteResult,
|
|
20
21
|
} from "#protocol";
|
|
21
|
-
import { ClientInteraction } from "#protocol";
|
|
22
22
|
import { ClientEndpointInitializer } from "./ClientEndpointInitializer.js";
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -31,6 +31,12 @@ export class ClientNodeInteraction implements Interactable<ActionContext> {
|
|
|
31
31
|
this.#node = node;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Read chosen attributes remotely from the node. Known data versions are automatically injected into the request to
|
|
36
|
+
* optimize the read.
|
|
37
|
+
* Therefore, the returned data only contains attributes that have changed since the last read or subscription.
|
|
38
|
+
* TODO: Allow control of data version injection and enrich response with attribute data missing in response due to data versioning.
|
|
39
|
+
*/
|
|
34
40
|
async *read(request: Read, context?: ActionContext): ReadResult {
|
|
35
41
|
request = this.structure.injectVersionFilters(request);
|
|
36
42
|
const interaction = await this.#connect();
|
|
@@ -38,6 +44,10 @@ export class ClientNodeInteraction implements Interactable<ActionContext> {
|
|
|
38
44
|
yield* this.structure.mutate(request, response);
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Subscribe to chosen attributes remotely from the node. Data are automatically updated in the storage and not
|
|
49
|
+
* returned. The subscription response message with the subscription id and the maxInterval is returned.
|
|
50
|
+
*/
|
|
41
51
|
async subscribe(request: Subscribe, context?: ActionContext): SubscribeResult {
|
|
42
52
|
const intermediateRequest: Subscribe = {
|
|
43
53
|
...this.structure.injectVersionFilters(request),
|
|
@@ -67,11 +77,15 @@ export class ClientNodeInteraction implements Interactable<ActionContext> {
|
|
|
67
77
|
this.#node.env.get(ClientInteraction).cancelSubscription(id);
|
|
68
78
|
}
|
|
69
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Write chosen attributes remotely to the node.
|
|
82
|
+
* The returned attribute write status information is returned.
|
|
83
|
+
*/
|
|
70
84
|
async write<T extends Write>(request: T, context?: ActionContext): WriteResult<T> {
|
|
71
85
|
return (await this.#connect()).write(request, context);
|
|
72
86
|
}
|
|
73
87
|
|
|
74
|
-
async *invoke(request:
|
|
88
|
+
async *invoke(request: ClientInvoke, context?: ActionContext): DecodedInvokeResult {
|
|
75
89
|
yield* (await this.#connect()).invoke(request, context);
|
|
76
90
|
}
|
|
77
91
|
|
package/src/node/client/Peers.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { Discovery } from "#behavior/system/controller/discovery/Discovery.js";
|
|
|
11
11
|
import { InstanceDiscovery } from "#behavior/system/controller/discovery/InstanceDiscovery.js";
|
|
12
12
|
import { EndpointContainer } from "#endpoint/properties/EndpointContainer.js";
|
|
13
13
|
import { CancelablePromise, Duration, ImplementationError, Logger, Minutes, Seconds, Time, Timestamp } from "#general";
|
|
14
|
+
import { ClientGroup } from "#node/ClientGroup.js";
|
|
14
15
|
import { InteractionServer } from "#node/index.js";
|
|
15
16
|
import { FabricManager, PeerAddress, PeerAddressStore } from "#protocol";
|
|
16
17
|
import { ServerNodeStore } from "#storage/server/ServerNodeStore.js";
|
|
@@ -58,6 +59,7 @@ export class Peers extends EndpointContainer<ClientNode> {
|
|
|
58
59
|
const factory = this.owner.env.get(ClientNodeFactory);
|
|
59
60
|
|
|
60
61
|
const clientStores = this.owner.env.get(ServerNodeStore).clientStores;
|
|
62
|
+
// Group nodes have an in-memory only store, so all nodes restored here are ClientNode
|
|
61
63
|
for (const id of clientStores.knownIds) {
|
|
62
64
|
this.add(
|
|
63
65
|
factory.create({
|
|
@@ -131,7 +133,7 @@ export class Peers extends EndpointContainer<ClientNode> {
|
|
|
131
133
|
if (!node) {
|
|
132
134
|
// We do not have that node till now, also not persisted, so create it
|
|
133
135
|
const factory = this.owner.env.get(ClientNodeFactory);
|
|
134
|
-
node = factory.create(options);
|
|
136
|
+
node = factory.create(options, peerAddress);
|
|
135
137
|
await node.construction;
|
|
136
138
|
this.add(node);
|
|
137
139
|
|
|
@@ -278,20 +280,33 @@ export class Peers extends EndpointContainer<ClientNode> {
|
|
|
278
280
|
|
|
279
281
|
class Factory extends ClientNodeFactory {
|
|
280
282
|
#owner: Peers;
|
|
283
|
+
#groupIdCounter = 0;
|
|
281
284
|
|
|
282
285
|
constructor(owner: Peers) {
|
|
283
286
|
super();
|
|
284
287
|
this.#owner = owner;
|
|
285
288
|
}
|
|
286
289
|
|
|
287
|
-
create(options: ClientNode.Options) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
+
create(options: ClientNode.Options, peerAddress?: PeerAddress) {
|
|
291
|
+
let node: ClientNode;
|
|
292
|
+
if (peerAddress !== undefined && PeerAddress.isGroup(peerAddress)) {
|
|
293
|
+
if (options.id === undefined) {
|
|
294
|
+
options.id = `group${++this.#groupIdCounter}`;
|
|
295
|
+
}
|
|
296
|
+
node = new ClientGroup({
|
|
297
|
+
...options,
|
|
298
|
+
owner: this.#owner.owner,
|
|
299
|
+
});
|
|
300
|
+
} else {
|
|
301
|
+
if (options.id === undefined) {
|
|
302
|
+
options.id = this.#owner.owner.env.get(ServerNodeStore).clientStores.allocateId();
|
|
303
|
+
}
|
|
304
|
+
node = new ClientNode({
|
|
305
|
+
...options,
|
|
306
|
+
owner: this.#owner.owner,
|
|
307
|
+
});
|
|
290
308
|
}
|
|
291
|
-
|
|
292
|
-
...options,
|
|
293
|
-
owner: this.#owner.owner,
|
|
294
|
-
});
|
|
309
|
+
|
|
295
310
|
node.construction.start();
|
|
296
311
|
return node;
|
|
297
312
|
}
|
|
@@ -35,7 +35,14 @@ import type {
|
|
|
35
35
|
InteractionSession,
|
|
36
36
|
NodeProtocol,
|
|
37
37
|
} from "#protocol";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
EventTypeProtocol,
|
|
40
|
+
FabricManager,
|
|
41
|
+
hasRemoteActor,
|
|
42
|
+
OccurrenceManager,
|
|
43
|
+
toWildcardOrHexPath,
|
|
44
|
+
Val,
|
|
45
|
+
} from "#protocol";
|
|
39
46
|
import {
|
|
40
47
|
AttributeId,
|
|
41
48
|
AttributePath,
|
|
@@ -554,7 +561,7 @@ function invokeCommand(
|
|
|
554
561
|
const context = session as ActionContext;
|
|
555
562
|
|
|
556
563
|
logger.info(
|
|
557
|
-
"Invoke",
|
|
564
|
+
"Invoke «",
|
|
558
565
|
Diagnostic.strong(`${path.toString()}.${command.name}`),
|
|
559
566
|
session.transaction.via,
|
|
560
567
|
requestDiagnostic,
|
|
@@ -629,13 +636,6 @@ function invokeCommand(
|
|
|
629
636
|
return result as MaybePromise<Val.Struct | undefined>;
|
|
630
637
|
}
|
|
631
638
|
|
|
632
|
-
function toWildcardOrHex(name: string, value: number | bigint | undefined) {
|
|
633
|
-
if (value === undefined) {
|
|
634
|
-
return "*";
|
|
635
|
-
}
|
|
636
|
-
return `${name}:0x${value.toString(16)}`;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
639
|
/**
|
|
640
640
|
* Resolve a path into a human readable textual form for logging
|
|
641
641
|
* TODO: Add a Diagnostic display formatter for this
|
|
@@ -656,35 +656,35 @@ function resolvePathForNode(node: NodeProtocol, path: AttributePath | EventPath
|
|
|
656
656
|
: undefined;
|
|
657
657
|
|
|
658
658
|
if (endpointId === undefined) {
|
|
659
|
-
return `*.${
|
|
659
|
+
return `*.${toWildcardOrHexPath("", clusterId)}.${toWildcardOrHexPath("", elementId)}${postString}`;
|
|
660
660
|
}
|
|
661
661
|
|
|
662
662
|
const endpoint = node[endpointId];
|
|
663
663
|
if (endpoint === undefined) {
|
|
664
|
-
return `${
|
|
664
|
+
return `${toWildcardOrHexPath("?", endpointId)}.${toWildcardOrHexPath("", clusterId)}.${toWildcardOrHexPath("", elementId)}${postString}`;
|
|
665
665
|
}
|
|
666
|
-
const endpointName =
|
|
666
|
+
const endpointName = toWildcardOrHexPath(endpoint.name, endpointId);
|
|
667
667
|
|
|
668
668
|
if (clusterId === undefined) {
|
|
669
|
-
return `${endpointName}.*.${
|
|
669
|
+
return `${endpointName}.*.${toWildcardOrHexPath("", elementId)}${postString}`;
|
|
670
670
|
}
|
|
671
671
|
|
|
672
672
|
const cluster = endpoint[clusterId];
|
|
673
673
|
if (cluster === undefined) {
|
|
674
|
-
return `${endpointName}.${
|
|
674
|
+
return `${endpointName}.${toWildcardOrHexPath("?", clusterId)}.${toWildcardOrHexPath("", elementId)}${postString}`;
|
|
675
675
|
}
|
|
676
|
-
const clusterName =
|
|
676
|
+
const clusterName = toWildcardOrHexPath(cluster.type.name, clusterId);
|
|
677
677
|
|
|
678
678
|
if (elementId !== undefined) {
|
|
679
679
|
if ("eventId" in path) {
|
|
680
680
|
const event = cluster.type.events[elementId];
|
|
681
|
-
return `${endpointName}.${clusterName}.${
|
|
681
|
+
return `${endpointName}.${clusterName}.${toWildcardOrHexPath(event?.name ?? "?", elementId)}${postString}`;
|
|
682
682
|
} else if ("attributeId" in path) {
|
|
683
683
|
const attribute = cluster.type.attributes[elementId];
|
|
684
|
-
return `${endpointName}.${clusterName}.${
|
|
684
|
+
return `${endpointName}.${clusterName}.${toWildcardOrHexPath(attribute?.name ?? "?", elementId)}${postString}`;
|
|
685
685
|
} else if ("commandId" in path) {
|
|
686
686
|
const command = cluster.type.commands[elementId];
|
|
687
|
-
return `${endpointName}.${clusterName}.${
|
|
687
|
+
return `${endpointName}.${clusterName}.${toWildcardOrHexPath(command?.name ?? "?", elementId)}${postString}`;
|
|
688
688
|
} else {
|
|
689
689
|
throw new ImplementationError("Invalid path");
|
|
690
690
|
}
|