@matter/protocol 0.16.0-alpha.0-20251016-b56cf5683 → 0.16.0-alpha.0-20251018-dd1ea6a8a
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/action/client/ClientInteraction.d.ts +10 -5
- package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/cjs/action/client/ClientInteraction.js +129 -18
- package/dist/cjs/action/client/ClientInteraction.js.map +1 -1
- package/dist/cjs/action/request/Invoke.d.ts +36 -8
- package/dist/cjs/action/request/Invoke.d.ts.map +1 -1
- package/dist/cjs/action/request/Invoke.js +80 -15
- package/dist/cjs/action/request/Invoke.js.map +1 -1
- package/dist/cjs/action/request/Read.d.ts +1 -1
- package/dist/cjs/action/request/Read.d.ts.map +1 -1
- package/dist/cjs/action/request/Read.js +10 -2
- package/dist/cjs/action/request/Read.js.map +1 -1
- package/dist/cjs/action/request/Specifier.d.ts +16 -1
- package/dist/cjs/action/request/Specifier.d.ts.map +1 -1
- package/dist/cjs/action/request/Specifier.js +56 -1
- package/dist/cjs/action/request/Specifier.js.map +1 -1
- package/dist/cjs/action/request/Write.d.ts +5 -3
- package/dist/cjs/action/request/Write.d.ts.map +1 -1
- package/dist/cjs/action/request/Write.js +52 -12
- package/dist/cjs/action/request/Write.js.map +1 -1
- package/dist/cjs/action/response/InvokeResult.d.ts +6 -0
- package/dist/cjs/action/response/InvokeResult.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionClient.js +91 -74
- package/dist/cjs/interaction/InteractionClient.js.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.d.ts +3 -2
- package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.js +10 -3
- package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
- package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
- package/dist/cjs/protocol/ExchangeManager.js +20 -17
- package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
- package/dist/cjs/protocol/MessageChannel.d.ts.map +1 -1
- package/dist/cjs/protocol/MessageChannel.js +1 -1
- package/dist/cjs/protocol/MessageChannel.js.map +1 -1
- package/dist/cjs/protocol/MessageExchange.d.ts +1 -0
- package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
- package/dist/cjs/protocol/MessageExchange.js +14 -4
- package/dist/cjs/protocol/MessageExchange.js.map +1 -1
- package/dist/cjs/protocol/ProtocolHandler.d.ts +7 -2
- package/dist/cjs/protocol/ProtocolHandler.d.ts.map +1 -1
- package/dist/cjs/securechannel/SecureChannelProtocol.d.ts +1 -1
- package/dist/cjs/securechannel/SecureChannelProtocol.d.ts.map +1 -1
- package/dist/cjs/securechannel/SecureChannelProtocol.js +2 -1
- package/dist/cjs/securechannel/SecureChannelProtocol.js.map +1 -1
- package/dist/cjs/session/GroupSession.d.ts +8 -1
- package/dist/cjs/session/GroupSession.d.ts.map +1 -1
- package/dist/cjs/session/GroupSession.js +10 -0
- package/dist/cjs/session/GroupSession.js.map +1 -1
- package/dist/cjs/session/NodeSession.d.ts +3 -1
- package/dist/cjs/session/NodeSession.d.ts.map +1 -1
- package/dist/cjs/session/NodeSession.js +13 -0
- package/dist/cjs/session/NodeSession.js.map +2 -2
- package/dist/cjs/session/SecureSession.d.ts +2 -0
- package/dist/cjs/session/SecureSession.d.ts.map +1 -1
- package/dist/cjs/session/SecureSession.js.map +1 -1
- package/dist/cjs/session/case/CaseClient.d.ts.map +1 -1
- package/dist/cjs/session/case/CaseClient.js +3 -15
- package/dist/cjs/session/case/CaseClient.js.map +1 -1
- package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
- package/dist/cjs/session/case/CaseServer.js +10 -18
- package/dist/cjs/session/case/CaseServer.js.map +1 -1
- package/dist/cjs/session/pase/PaseClient.js +1 -1
- package/dist/cjs/session/pase/PaseClient.js.map +1 -1
- package/dist/cjs/session/pase/PaseServer.d.ts.map +1 -1
- package/dist/cjs/session/pase/PaseServer.js +2 -2
- package/dist/cjs/session/pase/PaseServer.js.map +1 -1
- package/dist/esm/action/client/ClientInteraction.d.ts +10 -5
- package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/esm/action/client/ClientInteraction.js +140 -20
- package/dist/esm/action/client/ClientInteraction.js.map +1 -1
- package/dist/esm/action/request/Invoke.d.ts +36 -8
- package/dist/esm/action/request/Invoke.d.ts.map +1 -1
- package/dist/esm/action/request/Invoke.js +81 -16
- package/dist/esm/action/request/Invoke.js.map +1 -1
- package/dist/esm/action/request/Read.d.ts +1 -1
- package/dist/esm/action/request/Read.d.ts.map +1 -1
- package/dist/esm/action/request/Read.js +12 -4
- package/dist/esm/action/request/Read.js.map +1 -1
- package/dist/esm/action/request/Specifier.d.ts +16 -1
- package/dist/esm/action/request/Specifier.d.ts.map +1 -1
- package/dist/esm/action/request/Specifier.js +56 -1
- package/dist/esm/action/request/Specifier.js.map +1 -1
- package/dist/esm/action/request/Write.d.ts +5 -3
- package/dist/esm/action/request/Write.d.ts.map +1 -1
- package/dist/esm/action/request/Write.js +53 -13
- package/dist/esm/action/request/Write.js.map +1 -1
- package/dist/esm/action/response/InvokeResult.d.ts +6 -0
- package/dist/esm/action/response/InvokeResult.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionClient.js +92 -74
- package/dist/esm/interaction/InteractionClient.js.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.d.ts +3 -2
- package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.js +10 -3
- package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
- package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
- package/dist/esm/protocol/ExchangeManager.js +20 -17
- package/dist/esm/protocol/ExchangeManager.js.map +1 -1
- package/dist/esm/protocol/MessageChannel.d.ts.map +1 -1
- package/dist/esm/protocol/MessageChannel.js +2 -2
- package/dist/esm/protocol/MessageChannel.js.map +1 -1
- package/dist/esm/protocol/MessageExchange.d.ts +1 -0
- package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
- package/dist/esm/protocol/MessageExchange.js +14 -4
- package/dist/esm/protocol/MessageExchange.js.map +1 -1
- package/dist/esm/protocol/ProtocolHandler.d.ts +7 -2
- package/dist/esm/protocol/ProtocolHandler.d.ts.map +1 -1
- package/dist/esm/securechannel/SecureChannelProtocol.d.ts +1 -1
- package/dist/esm/securechannel/SecureChannelProtocol.d.ts.map +1 -1
- package/dist/esm/securechannel/SecureChannelProtocol.js +2 -1
- package/dist/esm/securechannel/SecureChannelProtocol.js.map +1 -1
- package/dist/esm/session/GroupSession.d.ts +8 -1
- package/dist/esm/session/GroupSession.d.ts.map +1 -1
- package/dist/esm/session/GroupSession.js +11 -1
- package/dist/esm/session/GroupSession.js.map +1 -1
- package/dist/esm/session/NodeSession.d.ts +3 -1
- package/dist/esm/session/NodeSession.d.ts.map +1 -1
- package/dist/esm/session/NodeSession.js +13 -0
- package/dist/esm/session/NodeSession.js.map +2 -2
- package/dist/esm/session/SecureSession.d.ts +2 -0
- package/dist/esm/session/SecureSession.d.ts.map +1 -1
- package/dist/esm/session/SecureSession.js.map +1 -1
- package/dist/esm/session/case/CaseClient.d.ts.map +1 -1
- package/dist/esm/session/case/CaseClient.js +5 -17
- package/dist/esm/session/case/CaseClient.js.map +1 -1
- package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
- package/dist/esm/session/case/CaseServer.js +12 -20
- package/dist/esm/session/case/CaseServer.js.map +1 -1
- package/dist/esm/session/pase/PaseClient.js +1 -1
- package/dist/esm/session/pase/PaseClient.js.map +1 -1
- package/dist/esm/session/pase/PaseServer.d.ts.map +1 -1
- package/dist/esm/session/pase/PaseServer.js +3 -2
- package/dist/esm/session/pase/PaseServer.js.map +1 -1
- package/package.json +6 -6
- package/src/action/client/ClientInteraction.ts +181 -27
- package/src/action/request/Invoke.ts +149 -27
- package/src/action/request/Read.ts +27 -7
- package/src/action/request/Specifier.ts +80 -1
- package/src/action/request/Write.ts +71 -19
- package/src/action/response/InvokeResult.ts +8 -0
- package/src/interaction/InteractionClient.ts +135 -96
- package/src/interaction/InteractionMessenger.ts +15 -3
- package/src/protocol/ExchangeManager.ts +27 -29
- package/src/protocol/MessageChannel.ts +2 -2
- package/src/protocol/MessageExchange.ts +18 -4
- package/src/protocol/ProtocolHandler.ts +7 -2
- package/src/securechannel/SecureChannelProtocol.ts +5 -1
- package/src/session/GroupSession.ts +12 -1
- package/src/session/NodeSession.ts +21 -0
- package/src/session/SecureSession.ts +2 -0
- package/src/session/case/CaseClient.ts +3 -23
- package/src/session/case/CaseServer.ts +15 -20
- package/src/session/pase/PaseClient.ts +1 -1
- package/src/session/pase/PaseServer.ts +3 -2
|
@@ -41,7 +41,7 @@ export namespace Specifier {
|
|
|
41
41
|
export type Command<C extends ClusterType = ClusterType> = ClusterType.Command | (string & keyof C["commands"]);
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* An event specifier may be the name of a
|
|
44
|
+
* An event specifier may be the name of a cluster event or an event object.
|
|
45
45
|
*/
|
|
46
46
|
export type Event<C extends ClusterType = ClusterType> = ClusterType.Event | (string & keyof C["events"]);
|
|
47
47
|
|
|
@@ -97,6 +97,26 @@ export namespace Specifier {
|
|
|
97
97
|
? CMD
|
|
98
98
|
: never;
|
|
99
99
|
|
|
100
|
+
export function commandFor<const C extends ClusterType, const CMD extends Specifier.Command<C>>(
|
|
101
|
+
cluster: C | undefined,
|
|
102
|
+
specifier: CMD,
|
|
103
|
+
): CommandFor<C, CMD> {
|
|
104
|
+
if (typeof specifier !== "string") {
|
|
105
|
+
return specifier as CommandFor<C, CMD>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (cluster === undefined) {
|
|
109
|
+
throw new MalformedRequestError(`Cannot designate command "${specifier}" without a cluster`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const command = cluster.commands?.[specifier];
|
|
113
|
+
if (command === undefined) {
|
|
114
|
+
throw new MalformedRequestError(`Cluster ${cluster.name} does not define command ${specifier}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return command as CommandFor<C, CMD>;
|
|
118
|
+
}
|
|
119
|
+
|
|
100
120
|
/**
|
|
101
121
|
* Extract an event object from a cluster and event specifier.
|
|
102
122
|
*/
|
|
@@ -144,3 +164,62 @@ export namespace Specifier {
|
|
|
144
164
|
return request.endpoint?.number;
|
|
145
165
|
}
|
|
146
166
|
}
|
|
167
|
+
|
|
168
|
+
export function toWildcardOrHexPath(name: string, value: number | bigint | undefined) {
|
|
169
|
+
if (value === undefined) {
|
|
170
|
+
return "*";
|
|
171
|
+
}
|
|
172
|
+
return `${name ? `${name}:` : ""}0x${value.toString(16)}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve a path into a human readable textual form for logging
|
|
177
|
+
* TODO: Add a Diagnostic display formatter for this
|
|
178
|
+
*/
|
|
179
|
+
export function resolvePathForSpecifier<const C extends ClusterType>(location: {
|
|
180
|
+
endpoint?: Specifier.Endpoint;
|
|
181
|
+
cluster?: Specifier.Cluster;
|
|
182
|
+
attribute?: Specifier.Attribute<Specifier.ClusterFor<C>>;
|
|
183
|
+
event?: Specifier.Event<Specifier.ClusterFor<C>>;
|
|
184
|
+
command?: Specifier.Command<Specifier.ClusterFor<C>>;
|
|
185
|
+
isUrgent?: boolean;
|
|
186
|
+
listIndex?: number | null;
|
|
187
|
+
}) {
|
|
188
|
+
const endpointId = Specifier.endpointIdOf(location);
|
|
189
|
+
const cluster = location.cluster ? Specifier.clusterFor(location.cluster) : undefined;
|
|
190
|
+
const attribute = location.attribute && cluster ? Specifier.attributeFor(cluster, location.attribute) : undefined;
|
|
191
|
+
const event = location.event && cluster ? Specifier.eventFor(cluster, location.event) : undefined;
|
|
192
|
+
const command = location.command && cluster ? Specifier.commandFor(cluster, location.command) : undefined;
|
|
193
|
+
const isUrgentString = "isUrgent" in location && location.isUrgent ? "!" : "";
|
|
194
|
+
const listIndexString = "listIndex" in location && location.listIndex === null ? "[ADD]" : "";
|
|
195
|
+
const postString = `${listIndexString}${isUrgentString}`;
|
|
196
|
+
|
|
197
|
+
const clusterId = cluster?.id;
|
|
198
|
+
const elementId = attribute ? attribute.id : event ? event.id : command ? command.requestId : undefined;
|
|
199
|
+
|
|
200
|
+
if (endpointId === undefined) {
|
|
201
|
+
return `*.${toWildcardOrHexPath("", clusterId)}.${toWildcardOrHexPath("", elementId)}${postString}`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const endpointName = toWildcardOrHexPath("", endpointId);
|
|
205
|
+
|
|
206
|
+
if (cluster === undefined || clusterId === undefined) {
|
|
207
|
+
return `${endpointName}.*.${toWildcardOrHexPath("", elementId)}${postString}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const clusterName = toWildcardOrHexPath(cluster.name, clusterId);
|
|
211
|
+
|
|
212
|
+
if (elementId !== undefined) {
|
|
213
|
+
if (event) {
|
|
214
|
+
return `${endpointName}.${clusterName}.${toWildcardOrHexPath(typeof location.event === "string" ? location.event : "?", elementId)}${postString}`;
|
|
215
|
+
} else if (attribute) {
|
|
216
|
+
return `${endpointName}.${clusterName}.${toWildcardOrHexPath(typeof location.attribute === "string" ? location.attribute : "?", elementId)}${postString}`;
|
|
217
|
+
} else if (command) {
|
|
218
|
+
return `${endpointName}.${clusterName}.${toWildcardOrHexPath(typeof location.command === "string" ? location.command : "?", elementId)}${postString}`;
|
|
219
|
+
} else {
|
|
220
|
+
return "unknown";
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
return `${endpointName}.${clusterName}.*${postString}`;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -4,14 +4,25 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { AccessControl } from "#clusters/access-control";
|
|
8
|
+
import { Diagnostic, Duration } from "#general";
|
|
9
|
+
import { Specification } from "#model";
|
|
10
|
+
import { ArraySchema, AttributeData, AttributeId, ClusterId, ClusterType, WriteRequest } from "#types";
|
|
9
11
|
import { MalformedRequestError } from "./MalformedRequestError.js";
|
|
10
|
-
import { Specifier } from "./Specifier.js";
|
|
12
|
+
import { resolvePathForSpecifier, Specifier } from "./Specifier.js";
|
|
13
|
+
|
|
14
|
+
const AclClusterId = AccessControl.Complete.id;
|
|
15
|
+
const AclAttributeId = AccessControl.Complete.attributes.acl.id;
|
|
16
|
+
const AclExtensionAttributeId = AccessControl.Complete.attributes.extension.id;
|
|
17
|
+
|
|
18
|
+
function isAclOrExtensionPath(path: { clusterId: ClusterId; attributeId: AttributeId }) {
|
|
19
|
+
const { clusterId, attributeId } = path;
|
|
20
|
+
return clusterId === AclClusterId && (attributeId === AclAttributeId || attributeId === AclExtensionAttributeId);
|
|
21
|
+
}
|
|
11
22
|
|
|
12
23
|
export interface Write extends WriteRequest {
|
|
13
|
-
/** Timeout only relevant for Client Interactions */
|
|
14
|
-
timeout?:
|
|
24
|
+
/** Timeout only relevant for Client Interactions with a required TimedRequest flagging */
|
|
25
|
+
timeout?: Duration;
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
/**
|
|
@@ -41,13 +52,25 @@ export function Write(optionsOrData: Write.Options | Write.Attribute, ...data: W
|
|
|
41
52
|
} else {
|
|
42
53
|
options = optionsOrData;
|
|
43
54
|
}
|
|
44
|
-
const { writes: writeRequests = [] } = options;
|
|
55
|
+
const { writes: writeRequests = [], timed, timeout, chunkLists } = options;
|
|
45
56
|
|
|
46
|
-
const result
|
|
47
|
-
timedRequest: !!
|
|
57
|
+
const result = {
|
|
58
|
+
timedRequest: !!timed || !!timeout,
|
|
59
|
+
timeout,
|
|
48
60
|
writeRequests,
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
moreChunkedMessages: false,
|
|
62
|
+
interactionModelRevision: options.interactionModelRevision ?? Specification.INTERACTION_MODEL_REVISION,
|
|
63
|
+
|
|
64
|
+
[Diagnostic.value]: () =>
|
|
65
|
+
Diagnostic.list(
|
|
66
|
+
data.map(entry => {
|
|
67
|
+
const { version, value } = entry;
|
|
68
|
+
return `${resolvePathForSpecifier(entry)} = ${Diagnostic.json(
|
|
69
|
+
value,
|
|
70
|
+
)}${version !== undefined ? `(version=${version})` : ""}`;
|
|
71
|
+
}),
|
|
72
|
+
),
|
|
73
|
+
} as Write;
|
|
51
74
|
|
|
52
75
|
for (const entry of data) {
|
|
53
76
|
reifyData(entry);
|
|
@@ -87,15 +110,43 @@ export function Write(optionsOrData: Write.Options | Write.Attribute, ...data: W
|
|
|
87
110
|
};
|
|
88
111
|
|
|
89
112
|
for (const specifier of attributes) {
|
|
113
|
+
const clusterId = cluster.id;
|
|
90
114
|
const attribute = Specifier.attributeFor(cluster, specifier);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
115
|
+
const { schema, id: attributeId } = attribute;
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
chunkLists &&
|
|
119
|
+
Array.isArray(value) &&
|
|
120
|
+
schema instanceof ArraySchema &&
|
|
121
|
+
// As implemented for Matter 1.4.2 in https://github.com/project-chip/connectedhomeip/pull/38263
|
|
122
|
+
// Acl writes will no longer be chunked by default, all others still
|
|
123
|
+
// Will be streamlined later ... see https://github.com/project-chip/connectedhomeip/issues/38270
|
|
124
|
+
!isAclOrExtensionPath({ clusterId, attributeId })
|
|
125
|
+
) {
|
|
126
|
+
writeRequests.push(
|
|
127
|
+
...schema
|
|
128
|
+
.encodeAsChunkedArray(value, { forWriteInteraction: true })
|
|
129
|
+
.map(({ element: data, listIndex }) => ({
|
|
130
|
+
path: {
|
|
131
|
+
...prototype.path,
|
|
132
|
+
attributeId: attribute.id,
|
|
133
|
+
listIndex,
|
|
134
|
+
},
|
|
135
|
+
data,
|
|
136
|
+
dataVersion,
|
|
137
|
+
})),
|
|
138
|
+
);
|
|
139
|
+
} else {
|
|
140
|
+
writeRequests.push({
|
|
141
|
+
...prototype,
|
|
142
|
+
path: {
|
|
143
|
+
...prototype.path,
|
|
144
|
+
attributeId: attribute.id,
|
|
145
|
+
},
|
|
146
|
+
data: attribute.schema.encodeTlv(value, { forWriteInteraction: true }),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
result.timedRequest ||= attribute.timed;
|
|
99
150
|
}
|
|
100
151
|
}
|
|
101
152
|
}
|
|
@@ -104,8 +155,9 @@ export namespace Write {
|
|
|
104
155
|
export interface Options {
|
|
105
156
|
writes?: AttributeData[];
|
|
106
157
|
timed?: boolean;
|
|
107
|
-
timeout?:
|
|
158
|
+
timeout?: Duration;
|
|
108
159
|
interactionModelRevision?: number;
|
|
160
|
+
chunkLists?: boolean;
|
|
109
161
|
}
|
|
110
162
|
|
|
111
163
|
/**
|
|
@@ -8,10 +8,14 @@ import type { ClusterId, CommandId, CommandPath, EndpointNumber, StatusCode, Tlv
|
|
|
8
8
|
|
|
9
9
|
export type InvokeResult = AsyncIterable<InvokeResult.Chunk>;
|
|
10
10
|
|
|
11
|
+
export type DecodedInvokeResult = AsyncIterable<InvokeResult.DecodedChunk>;
|
|
12
|
+
|
|
11
13
|
export namespace InvokeResult {
|
|
12
14
|
export type Chunk = Iterable<Data>;
|
|
15
|
+
export type DecodedChunk = Iterable<DecodedData>;
|
|
13
16
|
|
|
14
17
|
export type Data = CommandStatus | CommandResponse;
|
|
18
|
+
export type DecodedData = CommandStatus | DecodedCommandResponse;
|
|
15
19
|
|
|
16
20
|
export interface ConcreteCommandPath extends CommandPath {
|
|
17
21
|
endpointId: EndpointNumber;
|
|
@@ -33,4 +37,8 @@ export namespace InvokeResult {
|
|
|
33
37
|
commandRef?: number;
|
|
34
38
|
data: TlvStream;
|
|
35
39
|
}
|
|
40
|
+
|
|
41
|
+
export interface DecodedCommandResponse extends Omit<CommandResponse, "data"> {
|
|
42
|
+
data: any;
|
|
43
|
+
}
|
|
36
44
|
}
|
|
@@ -383,26 +383,6 @@ export class InteractionClient {
|
|
|
383
383
|
);
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
logger.debug(
|
|
387
|
-
`Sending read request: attributes: ${attributeRequests
|
|
388
|
-
?.map(path => resolveAttributeName(path))
|
|
389
|
-
.join(", ")} and events: ${eventRequests?.map(path => resolveEventName(path)).join(", ")}`,
|
|
390
|
-
);
|
|
391
|
-
if (dataVersionFilters !== undefined && dataVersionFilters?.length > 0) {
|
|
392
|
-
logger.debug(
|
|
393
|
-
`Using DataVersionFilters: ${dataVersionFilters
|
|
394
|
-
.map(({ endpointId, clusterId, dataVersion }) => `${endpointId}/${clusterId}=${dataVersion}`)
|
|
395
|
-
.join(", ")}`,
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
if (eventFilters !== undefined && eventFilters?.length > 0) {
|
|
399
|
-
logger.debug(
|
|
400
|
-
`Using event filters: ${eventFilters
|
|
401
|
-
.map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`)
|
|
402
|
-
.join(", ")}`,
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
386
|
const result = await this.withMessenger(async messenger => {
|
|
407
387
|
return await this.processReadRequest(
|
|
408
388
|
messenger,
|
|
@@ -560,12 +540,32 @@ export class InteractionClient {
|
|
|
560
540
|
oldValue?: any,
|
|
561
541
|
) => void,
|
|
562
542
|
): Promise<DecodedDataReport> {
|
|
563
|
-
const { attributeRequests, eventRequests } = request;
|
|
564
|
-
logger.debug(
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
543
|
+
const { attributeRequests, eventRequests, dataVersionFilters, eventFilters, isFabricFiltered } = request;
|
|
544
|
+
logger.debug(() => [
|
|
545
|
+
"Read »",
|
|
546
|
+
messenger.exchange.via,
|
|
547
|
+
Diagnostic.dict({
|
|
548
|
+
attributes: attributeRequests?.length
|
|
549
|
+
? attributeRequests?.map(path => resolveAttributeName(path)).join(", ")
|
|
550
|
+
: undefined,
|
|
551
|
+
events: eventRequests?.length
|
|
552
|
+
? eventRequests?.map(path => resolveEventName(path)).join(", ")
|
|
553
|
+
: undefined,
|
|
554
|
+
dataVersionFilters: dataVersionFilters?.length
|
|
555
|
+
? dataVersionFilters
|
|
556
|
+
.map(
|
|
557
|
+
({ path: { endpointId, clusterId }, dataVersion }) =>
|
|
558
|
+
`${endpointId}/${clusterId}=${dataVersion}`,
|
|
559
|
+
)
|
|
560
|
+
.join(", ")
|
|
561
|
+
: undefined,
|
|
562
|
+
eventFilters: eventFilters?.length
|
|
563
|
+
? eventFilters.map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`).join(", ")
|
|
564
|
+
: undefined,
|
|
565
|
+
fabricFiltered: isFabricFiltered,
|
|
566
|
+
}),
|
|
567
|
+
]);
|
|
568
|
+
|
|
569
569
|
// Send read request and combine all (potentially chunked) responses
|
|
570
570
|
await messenger.sendReadRequest(request);
|
|
571
571
|
const scope = ReadScope(request);
|
|
@@ -576,24 +576,32 @@ export class InteractionClient {
|
|
|
576
576
|
// Normalize and decode the response
|
|
577
577
|
const { attributeReports, attributeStatus, eventReports, eventStatus } = response;
|
|
578
578
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
579
|
+
if (attributeReports.length || eventReports.length || attributeStatus?.length || eventStatus?.length) {
|
|
580
|
+
logger.debug(() => [
|
|
581
|
+
"Read «",
|
|
582
|
+
messenger.exchange.via,
|
|
583
|
+
Diagnostic.dict({
|
|
584
|
+
attributes: attributeReports.length
|
|
585
|
+
? attributeReports
|
|
586
|
+
.map(({ path, value }) => `${resolveAttributeName(path)}=${serialize(value)}`)
|
|
587
|
+
.join(", ")
|
|
588
|
+
: undefined,
|
|
589
|
+
events: eventReports.length
|
|
590
|
+
? eventReports.map(({ path }) => resolveEventName(path)).join(", ")
|
|
591
|
+
: undefined,
|
|
592
|
+
attributeStatus: attributeStatus?.length
|
|
593
|
+
? attributeStatus.map(({ path }) => resolveAttributeName(path)).join(", ")
|
|
594
|
+
: undefined,
|
|
595
|
+
eventStatus: eventStatus?.length
|
|
596
|
+
? eventStatus.map(({ path }) => resolveEventName(path)).join(", ")
|
|
597
|
+
: undefined,
|
|
598
|
+
fabricFiltered: isFabricFiltered,
|
|
599
|
+
}),
|
|
600
|
+
]);
|
|
601
|
+
} else {
|
|
602
|
+
logger.debug("Read «", messenger.exchange.via, "empty response");
|
|
587
603
|
}
|
|
588
|
-
|
|
589
|
-
logData.push(`attributeErrors ${attributeStatus.map(({ path }) => resolveAttributeName(path)).join(", ")}`);
|
|
590
|
-
}
|
|
591
|
-
if (eventStatus !== undefined && eventStatus.length > 0) {
|
|
592
|
-
logData.push(`eventErrors ${eventStatus.map(({ path }) => resolveEventName(path)).join(", ")}`);
|
|
593
|
-
}
|
|
594
|
-
logger.debug(
|
|
595
|
-
logData.length ? `Received read response with ${logData.join(", ")}` : "Received empty read response",
|
|
596
|
-
);
|
|
604
|
+
|
|
597
605
|
return response;
|
|
598
606
|
}
|
|
599
607
|
|
|
@@ -674,16 +682,7 @@ export class InteractionClient {
|
|
|
674
682
|
throw new ImplementationError("Not all attribute write paths are valid for group address writes.");
|
|
675
683
|
}
|
|
676
684
|
}
|
|
677
|
-
|
|
678
|
-
`Sending write request: ${attributes
|
|
679
|
-
.map(
|
|
680
|
-
({ endpointId, clusterId, attribute: { id }, value, dataVersion }) =>
|
|
681
|
-
`${resolveAttributeName({ endpointId, clusterId, attributeId: id })} = ${Diagnostic.json(
|
|
682
|
-
value,
|
|
683
|
-
)} (version=${dataVersion})`,
|
|
684
|
-
)
|
|
685
|
-
.join(", ")}`,
|
|
686
|
-
);
|
|
685
|
+
|
|
687
686
|
// TODO Add multi message write handling with streamed encoding
|
|
688
687
|
const writeRequests = attributes.flatMap(
|
|
689
688
|
({ endpointId, clusterId, attribute: { id, schema }, value, dataVersion }) => {
|
|
@@ -727,6 +726,21 @@ export class InteractionClient {
|
|
|
727
726
|
await messenger.sendTimedRequest(timedRequestTimeout);
|
|
728
727
|
}
|
|
729
728
|
|
|
729
|
+
logger.debug(() => [
|
|
730
|
+
"Write »",
|
|
731
|
+
messenger.exchange.via,
|
|
732
|
+
Diagnostic.dict({
|
|
733
|
+
attributes: attributes
|
|
734
|
+
.map(
|
|
735
|
+
({ endpointId, clusterId, attribute: { id }, value, dataVersion }) =>
|
|
736
|
+
`${resolveAttributeName({ endpointId, clusterId, attributeId: id })} = ${Diagnostic.json(
|
|
737
|
+
value,
|
|
738
|
+
)} (version=${dataVersion})`,
|
|
739
|
+
)
|
|
740
|
+
.join(", "),
|
|
741
|
+
}),
|
|
742
|
+
]);
|
|
743
|
+
|
|
730
744
|
return await messenger.sendWriteCommand({
|
|
731
745
|
suppressResponse,
|
|
732
746
|
timedRequest,
|
|
@@ -798,14 +812,6 @@ export class InteractionClient {
|
|
|
798
812
|
}
|
|
799
813
|
}
|
|
800
814
|
|
|
801
|
-
logger.debug(
|
|
802
|
-
`Sending subscribe request for attribute: ${resolveAttributeName({
|
|
803
|
-
endpointId,
|
|
804
|
-
clusterId,
|
|
805
|
-
attributeId,
|
|
806
|
-
})}${knownDataVersion !== undefined ? ` (knownDataVersion=${knownDataVersion})` : ""} with minInterval=${minIntervalFloorSeconds}s/maxInterval=${maxIntervalCeilingSeconds}s`,
|
|
807
|
-
);
|
|
808
|
-
|
|
809
815
|
const request: SubscribeRequest = {
|
|
810
816
|
interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
|
|
811
817
|
attributeRequests: [{ endpointId, clusterId, attributeId }],
|
|
@@ -829,6 +835,18 @@ export class InteractionClient {
|
|
|
829
835
|
report: DecodedDataReport;
|
|
830
836
|
maximumPeerResponseTime: Duration;
|
|
831
837
|
}>(async messenger => {
|
|
838
|
+
logger.debug(() => [
|
|
839
|
+
"Subscribe »",
|
|
840
|
+
messenger.exchange.via,
|
|
841
|
+
Diagnostic.dict({
|
|
842
|
+
attributes: resolveAttributeName({ endpointId, clusterId, attributeId }),
|
|
843
|
+
dataVersionFilter: knownDataVersion,
|
|
844
|
+
fabricFiltered: isFabricFiltered,
|
|
845
|
+
minInterval: Duration.format(Seconds(minIntervalFloorSeconds)),
|
|
846
|
+
maxInterval: Duration.format(Seconds(maxIntervalCeilingSeconds)),
|
|
847
|
+
}),
|
|
848
|
+
]);
|
|
849
|
+
|
|
832
850
|
await messenger.sendSubscribeRequest(request);
|
|
833
851
|
const { subscribeResponse, report } = await messenger.readAggregateSubscribeResponse();
|
|
834
852
|
return {
|
|
@@ -913,10 +931,6 @@ export class InteractionClient {
|
|
|
913
931
|
} = options;
|
|
914
932
|
const { id: eventId } = event;
|
|
915
933
|
|
|
916
|
-
logger.debug(
|
|
917
|
-
`Sending subscribe request for event: ${resolveEventName({ endpointId, clusterId, eventId })} with minInterval=${minIntervalFloor}/maxInterval=${maxIntervalCeiling}`,
|
|
918
|
-
);
|
|
919
|
-
|
|
920
934
|
const {
|
|
921
935
|
report,
|
|
922
936
|
subscribeResponse: { subscriptionId, maxInterval },
|
|
@@ -926,6 +940,17 @@ export class InteractionClient {
|
|
|
926
940
|
report: DecodedDataReport;
|
|
927
941
|
maximumPeerResponseTime: Duration;
|
|
928
942
|
}>(async messenger => {
|
|
943
|
+
logger.debug(() => [
|
|
944
|
+
"Subscribe »",
|
|
945
|
+
messenger.exchange.via,
|
|
946
|
+
Diagnostic.dict({
|
|
947
|
+
events: resolveEventName({ endpointId, clusterId, eventId }),
|
|
948
|
+
fabricFiltered: isFabricFiltered,
|
|
949
|
+
minInterval: Duration.format(minIntervalFloor),
|
|
950
|
+
maxInterval: Duration.format(maxIntervalCeiling),
|
|
951
|
+
}),
|
|
952
|
+
]);
|
|
953
|
+
|
|
929
954
|
await messenger.sendSubscribeRequest({
|
|
930
955
|
interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
|
|
931
956
|
eventRequests: [{ endpointId, clusterId, eventId, isUrgent }],
|
|
@@ -1060,28 +1085,6 @@ export class InteractionClient {
|
|
|
1060
1085
|
}
|
|
1061
1086
|
}
|
|
1062
1087
|
|
|
1063
|
-
logger.debug(
|
|
1064
|
-
`Sending subscribe request: attributes: ${attributeRequests
|
|
1065
|
-
.map(path => resolveAttributeName(path))
|
|
1066
|
-
.join(
|
|
1067
|
-
", ",
|
|
1068
|
-
)} and events: ${eventRequests.map(path => resolveEventName(path)).join(", ")}, keepSubscriptions=${keepSubscriptions} with minInterval=${minIntervalFloorSeconds}s/maxInterval=${maxIntervalCeilingSeconds}s`,
|
|
1069
|
-
);
|
|
1070
|
-
if (dataVersionFilters !== undefined && dataVersionFilters?.length > 0) {
|
|
1071
|
-
logger.debug(
|
|
1072
|
-
`Using DataVersionFilters: ${dataVersionFilters
|
|
1073
|
-
.map(({ endpointId, clusterId, dataVersion }) => `${endpointId}/${clusterId}=${dataVersion}`)
|
|
1074
|
-
.join(", ")}`,
|
|
1075
|
-
);
|
|
1076
|
-
}
|
|
1077
|
-
if (eventFilters !== undefined && eventFilters?.length > 0) {
|
|
1078
|
-
logger.debug(
|
|
1079
|
-
`Using event filters: ${eventFilters
|
|
1080
|
-
.map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`)
|
|
1081
|
-
.join(", ")}`,
|
|
1082
|
-
);
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
1088
|
const request: SubscribeRequest = {
|
|
1086
1089
|
interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
|
|
1087
1090
|
attributeRequests,
|
|
@@ -1108,10 +1111,48 @@ export class InteractionClient {
|
|
|
1108
1111
|
report: DecodedDataReport;
|
|
1109
1112
|
maximumPeerResponseTime: Duration;
|
|
1110
1113
|
}>(async messenger => {
|
|
1114
|
+
logger.debug(() => [
|
|
1115
|
+
"Subscribe »",
|
|
1116
|
+
messenger.exchange.via,
|
|
1117
|
+
Diagnostic.dict({
|
|
1118
|
+
attributes: attributeRequests.length
|
|
1119
|
+
? attributeRequests.map(path => resolveAttributeName(path)).join(", ")
|
|
1120
|
+
: undefined,
|
|
1121
|
+
events: eventRequests.length
|
|
1122
|
+
? eventRequests.map(path => resolveEventName(path)).join(", ")
|
|
1123
|
+
: undefined,
|
|
1124
|
+
dataVersionFilter: dataVersionFilters?.length
|
|
1125
|
+
? dataVersionFilters
|
|
1126
|
+
.map(
|
|
1127
|
+
({ endpointId, clusterId, dataVersion }) =>
|
|
1128
|
+
`${endpointId}/${clusterId}=${dataVersion}`,
|
|
1129
|
+
)
|
|
1130
|
+
.join(", ")
|
|
1131
|
+
: undefined,
|
|
1132
|
+
eventFilters: eventFilters?.length
|
|
1133
|
+
? eventFilters.map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`).join(", ")
|
|
1134
|
+
: undefined,
|
|
1135
|
+
fabricFiltered: isFabricFiltered,
|
|
1136
|
+
keepSubscriptions,
|
|
1137
|
+
minInterval: Duration.format(Seconds(minIntervalFloorSeconds)),
|
|
1138
|
+
maxInterval: Duration.format(Seconds(maxIntervalCeilingSeconds)),
|
|
1139
|
+
}),
|
|
1140
|
+
]);
|
|
1141
|
+
|
|
1111
1142
|
await messenger.sendSubscribeRequest(request);
|
|
1112
1143
|
const { subscribeResponse, report } = await messenger.readAggregateSubscribeResponse(attributeReports =>
|
|
1113
1144
|
this.processAttributeUpdates(scope, attributeReports, attributeListener),
|
|
1114
1145
|
);
|
|
1146
|
+
|
|
1147
|
+
logger.info(
|
|
1148
|
+
"Subscription successful «",
|
|
1149
|
+
messenger.exchange.via,
|
|
1150
|
+
Diagnostic.dict({
|
|
1151
|
+
subId: subscribeResponse.subscriptionId,
|
|
1152
|
+
maxInterval: Duration.format(Seconds(subscribeResponse.maxInterval)),
|
|
1153
|
+
}),
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1115
1156
|
return {
|
|
1116
1157
|
subscribeResponse,
|
|
1117
1158
|
report,
|
|
@@ -1119,8 +1160,6 @@ export class InteractionClient {
|
|
|
1119
1160
|
};
|
|
1120
1161
|
}, executeQueued);
|
|
1121
1162
|
|
|
1122
|
-
logger.info(`Subscription successfully initialized with ID ${subscriptionId} and maxInterval ${maxInterval}s.`);
|
|
1123
|
-
|
|
1124
1163
|
const subscriptionListener = async (dataReport: {
|
|
1125
1164
|
attributeReports?: DecodedAttributeReportValue<any>[];
|
|
1126
1165
|
eventReports?: DecodedEventReportValue<any>[];
|
|
@@ -1139,9 +1178,7 @@ export class InteractionClient {
|
|
|
1139
1178
|
if (eventReports !== undefined) {
|
|
1140
1179
|
let maxEventNumber = this.#nodeStore?.maxEventNumber ?? eventReports[0].events[0].eventNumber;
|
|
1141
1180
|
eventReports.forEach(data => {
|
|
1142
|
-
logger.debug(
|
|
1143
|
-
`Received event update: ${resolveEventName(data.path)}: ${Diagnostic.json(data.events)}`,
|
|
1144
|
-
);
|
|
1181
|
+
logger.debug(`Event update « ${resolveEventName(data.path)}: ${Diagnostic.json(data.events)}`);
|
|
1145
1182
|
const { events } = data;
|
|
1146
1183
|
|
|
1147
1184
|
maxEventNumber =
|
|
@@ -1202,7 +1239,9 @@ export class InteractionClient {
|
|
|
1202
1239
|
version,
|
|
1203
1240
|
} = data;
|
|
1204
1241
|
|
|
1205
|
-
if (value === undefined)
|
|
1242
|
+
if (value === undefined) {
|
|
1243
|
+
throw new MatterFlowError("Received empty subscription result value.");
|
|
1244
|
+
}
|
|
1206
1245
|
const { value: oldValue, version: oldVersion } =
|
|
1207
1246
|
this.#nodeStore?.retrieveAttribute(endpointId, clusterId, attributeId) ?? {};
|
|
1208
1247
|
const changed = oldValue !== undefined ? !isDeepEqual(oldValue, value) : undefined;
|
|
@@ -1210,7 +1249,7 @@ export class InteractionClient {
|
|
|
1210
1249
|
await this.#nodeStore?.persistAttributes([data], scope);
|
|
1211
1250
|
}
|
|
1212
1251
|
logger.debug(
|
|
1213
|
-
`
|
|
1252
|
+
`Attribute update «${changed ? " (value changed)" : ""}: ${resolveAttributeName({
|
|
1214
1253
|
endpointId,
|
|
1215
1254
|
clusterId,
|
|
1216
1255
|
attributeId,
|
|
@@ -1355,7 +1394,7 @@ export class InteractionClient {
|
|
|
1355
1394
|
}
|
|
1356
1395
|
const response = responseSchema.decodeTlv(commandFields);
|
|
1357
1396
|
logger.debug(
|
|
1358
|
-
`
|
|
1397
|
+
`Invoke « ${resolveCommandName({
|
|
1359
1398
|
endpointId,
|
|
1360
1399
|
clusterId,
|
|
1361
1400
|
commandId: requestId,
|
|
@@ -1429,7 +1468,7 @@ export class InteractionClient {
|
|
|
1429
1468
|
}, executeQueued);
|
|
1430
1469
|
|
|
1431
1470
|
logger.debug(
|
|
1432
|
-
`Invoke successful
|
|
1471
|
+
`Invoke successful « ${resolveCommandName({
|
|
1433
1472
|
endpointId,
|
|
1434
1473
|
clusterId,
|
|
1435
1474
|
commandId: requestId,
|
|
@@ -105,7 +105,19 @@ const DATA_REPORT_MAX_QUEUED_ATTRIBUTE_MESSAGES = 20;
|
|
|
105
105
|
const DATA_REPORT_MIN_AVAILABLE_BYTES_BEFORE_SENDING = 40;
|
|
106
106
|
|
|
107
107
|
class InteractionMessenger {
|
|
108
|
-
|
|
108
|
+
#exchange: MessageExchange;
|
|
109
|
+
|
|
110
|
+
constructor(exchange: MessageExchange) {
|
|
111
|
+
this.#exchange = exchange;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get exchange() {
|
|
115
|
+
return this.#exchange;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
protected set exchange(value: MessageExchange) {
|
|
119
|
+
this.#exchange = value;
|
|
120
|
+
}
|
|
109
121
|
|
|
110
122
|
send(messageType: number, payload: Bytes, options?: ExchangeSendOptions) {
|
|
111
123
|
return this.exchange.send(messageType, payload, options);
|
|
@@ -197,8 +209,8 @@ class InteractionMessenger {
|
|
|
197
209
|
);
|
|
198
210
|
}
|
|
199
211
|
|
|
200
|
-
|
|
201
|
-
return this.exchange.
|
|
212
|
+
get session() {
|
|
213
|
+
return this.exchange.session;
|
|
202
214
|
}
|
|
203
215
|
}
|
|
204
216
|
|