@matter/protocol 0.13.1-alpha.0-20250509-28e1567e1 → 0.13.1-alpha.0-20250515-a4c61c546
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/protocols.d.ts +59 -2
- package/dist/cjs/action/protocols.d.ts.map +1 -1
- package/dist/cjs/action/request/Read.d.ts +2 -2
- package/dist/cjs/action/request/Read.d.ts.map +1 -1
- package/dist/cjs/action/request/Read.js +4 -4
- package/dist/cjs/action/request/Read.js.map +1 -1
- package/dist/cjs/action/response/ReadResult.d.ts +6 -2
- package/dist/cjs/action/response/ReadResult.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeResponse.d.ts +46 -17
- package/dist/cjs/action/server/AttributeResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeResponse.js +128 -110
- package/dist/cjs/action/server/AttributeResponse.js.map +2 -2
- package/dist/cjs/action/server/AttributeSubscriptionResponse.d.ts +36 -0
- package/dist/cjs/action/server/AttributeSubscriptionResponse.d.ts.map +1 -0
- package/dist/cjs/action/server/AttributeSubscriptionResponse.js +86 -0
- package/dist/cjs/action/server/AttributeSubscriptionResponse.js.map +6 -0
- package/dist/cjs/action/server/DataResponse.d.ts +45 -0
- package/dist/cjs/action/server/DataResponse.d.ts.map +1 -0
- package/dist/cjs/action/server/DataResponse.js +69 -0
- package/dist/cjs/action/server/DataResponse.js.map +6 -0
- package/dist/cjs/action/server/EventResponse.d.ts +28 -0
- package/dist/cjs/action/server/EventResponse.d.ts.map +1 -0
- package/dist/cjs/action/server/EventResponse.js +318 -0
- package/dist/cjs/action/server/EventResponse.js.map +6 -0
- package/dist/cjs/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/cjs/action/server/ServerInteraction.js +15 -2
- package/dist/cjs/action/server/ServerInteraction.js.map +1 -1
- package/dist/cjs/action/server/index.d.ts +3 -0
- package/dist/cjs/action/server/index.d.ts.map +1 -1
- package/dist/cjs/action/server/index.js +3 -0
- package/dist/cjs/action/server/index.js.map +1 -1
- package/dist/cjs/events/OccurrenceManager.d.ts +20 -11
- package/dist/cjs/events/OccurrenceManager.d.ts.map +1 -1
- package/dist/cjs/events/OccurrenceManager.js +113 -74
- package/dist/cjs/events/OccurrenceManager.js.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.d.ts +14 -2
- package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.js +87 -3
- package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
- package/dist/cjs/interaction/index.d.ts +0 -1
- package/dist/cjs/interaction/index.d.ts.map +1 -1
- package/dist/cjs/interaction/index.js +0 -1
- package/dist/cjs/interaction/index.js.map +1 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.js +1 -1
- package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
- package/dist/cjs/protocol/MessageExchange.js +11 -1
- package/dist/cjs/protocol/MessageExchange.js.map +1 -1
- package/dist/esm/action/protocols.d.ts +59 -2
- package/dist/esm/action/protocols.d.ts.map +1 -1
- package/dist/esm/action/request/Read.d.ts +2 -2
- package/dist/esm/action/request/Read.d.ts.map +1 -1
- package/dist/esm/action/request/Read.js +4 -4
- package/dist/esm/action/request/Read.js.map +1 -1
- package/dist/esm/action/response/ReadResult.d.ts +6 -2
- package/dist/esm/action/response/ReadResult.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeResponse.d.ts +46 -17
- package/dist/esm/action/server/AttributeResponse.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeResponse.js +129 -113
- package/dist/esm/action/server/AttributeResponse.js.map +1 -1
- package/dist/esm/action/server/AttributeSubscriptionResponse.d.ts +36 -0
- package/dist/esm/action/server/AttributeSubscriptionResponse.d.ts.map +1 -0
- package/dist/esm/action/server/AttributeSubscriptionResponse.js +66 -0
- package/dist/esm/action/server/AttributeSubscriptionResponse.js.map +6 -0
- package/dist/esm/action/server/DataResponse.d.ts +45 -0
- package/dist/esm/action/server/DataResponse.d.ts.map +1 -0
- package/dist/esm/action/server/DataResponse.js +49 -0
- package/dist/esm/action/server/DataResponse.js.map +6 -0
- package/dist/esm/action/server/EventResponse.d.ts +28 -0
- package/dist/esm/action/server/EventResponse.d.ts.map +1 -0
- package/dist/esm/action/server/EventResponse.js +305 -0
- package/dist/esm/action/server/EventResponse.js.map +6 -0
- package/dist/esm/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/esm/action/server/ServerInteraction.js +16 -3
- package/dist/esm/action/server/ServerInteraction.js.map +1 -1
- package/dist/esm/action/server/index.d.ts +3 -0
- package/dist/esm/action/server/index.d.ts.map +1 -1
- package/dist/esm/action/server/index.js +3 -0
- package/dist/esm/action/server/index.js.map +1 -1
- package/dist/esm/events/OccurrenceManager.d.ts +20 -11
- package/dist/esm/events/OccurrenceManager.d.ts.map +1 -1
- package/dist/esm/events/OccurrenceManager.js +117 -80
- package/dist/esm/events/OccurrenceManager.js.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.d.ts +14 -2
- package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.js +87 -3
- package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
- package/dist/esm/interaction/index.d.ts +0 -1
- package/dist/esm/interaction/index.d.ts.map +1 -1
- package/dist/esm/interaction/index.js +0 -1
- package/dist/esm/interaction/index.js.map +1 -1
- package/dist/esm/peer/ControllerCommissioningFlow.js +1 -1
- package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
- package/dist/esm/protocol/MessageExchange.js +11 -1
- package/dist/esm/protocol/MessageExchange.js.map +1 -1
- package/package.json +6 -6
- package/src/action/protocols.ts +68 -2
- package/src/action/request/Read.ts +2 -2
- package/src/action/response/ReadResult.ts +8 -1
- package/src/action/server/AttributeResponse.ts +145 -118
- package/src/action/server/AttributeSubscriptionResponse.ts +90 -0
- package/src/action/server/DataResponse.ts +70 -0
- package/src/action/server/EventResponse.ts +381 -0
- package/src/action/server/ServerInteraction.ts +18 -4
- package/src/action/server/index.ts +3 -0
- package/src/events/OccurrenceManager.ts +126 -100
- package/src/interaction/InteractionMessenger.ts +93 -8
- package/src/interaction/index.ts +0 -1
- package/src/peer/ControllerCommissioningFlow.ts +1 -1
- package/src/protocol/MessageExchange.ts +13 -1
- package/dist/cjs/interaction/ServerSubscription.d.ts +0 -116
- package/dist/cjs/interaction/ServerSubscription.d.ts.map +0 -1
- package/dist/cjs/interaction/ServerSubscription.js +0 -778
- package/dist/cjs/interaction/ServerSubscription.js.map +0 -6
- package/dist/esm/interaction/ServerSubscription.d.ts +0 -116
- package/dist/esm/interaction/ServerSubscription.d.ts.map +0 -1
- package/dist/esm/interaction/ServerSubscription.js +0 -778
- package/dist/esm/interaction/ServerSubscription.js.map +0 -6
- package/src/interaction/ServerSubscription.ts +0 -1038
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Project CHIP Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ClusterProtocol, EndpointProtocol, EventTypeProtocol, NodeProtocol } from "#action/protocols.js";
|
|
8
|
+
import { Read } from "#action/request/Read.js";
|
|
9
|
+
import { ReadResult } from "#action/response/ReadResult.js";
|
|
10
|
+
import { AccessControl } from "#action/server/AccessControl.js";
|
|
11
|
+
import { DataResponse, FallbackLimits } from "#action/server/DataResponse.js";
|
|
12
|
+
import { NumberedOccurrence } from "#events/index.js";
|
|
13
|
+
import { InternalError, isObject, Logger } from "#general";
|
|
14
|
+
import { DataModelPath, ElementTag, EventModel } from "#model";
|
|
15
|
+
import {
|
|
16
|
+
EventNumber,
|
|
17
|
+
EventPath,
|
|
18
|
+
FabricIndex,
|
|
19
|
+
NodeId,
|
|
20
|
+
Status,
|
|
21
|
+
StatusCode,
|
|
22
|
+
StatusResponseError,
|
|
23
|
+
TlvSchema,
|
|
24
|
+
} from "#types";
|
|
25
|
+
|
|
26
|
+
const logger = Logger.get("EventResponse");
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Implements read of event data for Matter "read" and "subscribe" interactions.
|
|
30
|
+
*
|
|
31
|
+
* We collect all allowed event paths at first and then iterate over all events and use the ones matching to the paths.
|
|
32
|
+
*
|
|
33
|
+
* TODO - profile; ensure nested functions are properly JITed and/or inlined
|
|
34
|
+
*/
|
|
35
|
+
export class EventResponse<
|
|
36
|
+
SessionT extends AccessControl.Session = AccessControl.Session,
|
|
37
|
+
> extends DataResponse<SessionT> {
|
|
38
|
+
// Normalized Event Filter to just our node-id
|
|
39
|
+
#eventMinVersion?: EventNumber;
|
|
40
|
+
|
|
41
|
+
// The Fabric filtering is done when we read the data from OccurrenceManager, so we can determine the parameter once
|
|
42
|
+
#filteredForFabricIndex?: FabricIndex;
|
|
43
|
+
|
|
44
|
+
// The following state updates as data producers execute. This serves both to convey state between functions and as
|
|
45
|
+
// a cache between producers that touch the same endpoint and/or cluster
|
|
46
|
+
#currentEndpoint?: EndpointProtocol;
|
|
47
|
+
#currentCluster?: ClusterProtocol;
|
|
48
|
+
|
|
49
|
+
// Collected allowed and existing event paths to consider when reading events
|
|
50
|
+
#allowedEventPaths = new Map<string, TlvSchema<unknown>>();
|
|
51
|
+
|
|
52
|
+
// Count how many attribute status (on error) and attribute values (on success) we have emitted
|
|
53
|
+
#statusCount = 0;
|
|
54
|
+
#valueCount = 0;
|
|
55
|
+
|
|
56
|
+
constructor(node: NodeProtocol, session: SessionT) {
|
|
57
|
+
super(node, session);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async *process({
|
|
61
|
+
eventFilters,
|
|
62
|
+
eventRequests,
|
|
63
|
+
isFabricFiltered,
|
|
64
|
+
}: Read.Events): AsyncGenerator<ReadResult.Chunk, void, void> {
|
|
65
|
+
const nodeId = this.session.fabric === undefined ? NodeId.UNSPECIFIED_NODE_ID : this.nodeId;
|
|
66
|
+
|
|
67
|
+
if (eventFilters !== undefined) {
|
|
68
|
+
for (const { nodeId: filterNodeId, eventMin } of eventFilters) {
|
|
69
|
+
if (filterNodeId === undefined || filterNodeId === nodeId) {
|
|
70
|
+
this.#eventMinVersion = EventNumber(eventMin);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (isFabricFiltered) {
|
|
77
|
+
this.#filteredForFabricIndex = this.session.fabric ?? FabricIndex.NO_FABRIC;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// This path contributes an event value
|
|
81
|
+
// Register paths
|
|
82
|
+
for (const path of eventRequests) {
|
|
83
|
+
if (path.endpointId === undefined || path.clusterId === undefined || path.eventId === undefined) {
|
|
84
|
+
this.#addWildcard(path);
|
|
85
|
+
} else {
|
|
86
|
+
const status = this.#addConcrete(path as ReadResult.ConcreteEventPath);
|
|
87
|
+
if (status !== undefined) {
|
|
88
|
+
// This path is not valid, so emit a status response
|
|
89
|
+
yield [status];
|
|
90
|
+
this.#statusCount++;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Perform actual read of all events
|
|
96
|
+
for await (const data of this.#readAllowedEvents()) {
|
|
97
|
+
yield [data];
|
|
98
|
+
this.#valueCount++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get counts() {
|
|
103
|
+
return {
|
|
104
|
+
status: this.#statusCount,
|
|
105
|
+
value: this.#valueCount,
|
|
106
|
+
existent: this.#allowedEventPaths.size,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Guarded accessor for this.#currentEndpoint. This should never be undefined */
|
|
111
|
+
get #guardedCurrentEndpoint() {
|
|
112
|
+
if (this.#currentEndpoint === undefined) {
|
|
113
|
+
throw new InternalError("currentEndpoint is not set. Should never happen");
|
|
114
|
+
}
|
|
115
|
+
return this.#currentEndpoint;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Guarded accessor for this.#currentCluster. This should never be undefined */
|
|
119
|
+
get #guardedCurrentCluster(): ClusterProtocol {
|
|
120
|
+
if (this.#currentCluster === undefined) {
|
|
121
|
+
throw new InternalError("currentCluster is not set. Should never happen");
|
|
122
|
+
}
|
|
123
|
+
return this.#currentCluster;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Validate a wildcard path and update the internal state.
|
|
128
|
+
*/
|
|
129
|
+
#addWildcard(path: EventPath) {
|
|
130
|
+
const { nodeId, clusterId, endpointId, eventId } = path;
|
|
131
|
+
|
|
132
|
+
if (clusterId === undefined && eventId !== undefined) {
|
|
133
|
+
throw new StatusResponseError("Illegal read request with wildcard cluster ID", StatusCode.InvalidAction);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (nodeId !== undefined && nodeId !== this.nodeId) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (endpointId === undefined) {
|
|
141
|
+
for (const endpoint of this.node) {
|
|
142
|
+
this.#addEndpointForWildcard(endpoint, path);
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const endpoint = this.node[endpointId];
|
|
148
|
+
if (endpoint) {
|
|
149
|
+
return this.#addEndpointForWildcard(endpoint, path);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Validate a concrete path and update the internal state.
|
|
155
|
+
*/
|
|
156
|
+
#addConcrete(path: ReadResult.ConcreteEventPath) {
|
|
157
|
+
const { nodeId, endpointId, clusterId, eventId } = path;
|
|
158
|
+
if (nodeId !== undefined && this.nodeId !== nodeId) {
|
|
159
|
+
return this.#asStatus(path, Status.UnsupportedNode);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Resolve path elements
|
|
163
|
+
const endpoint = this.node[endpointId];
|
|
164
|
+
const cluster = endpoint?.[clusterId];
|
|
165
|
+
const event = cluster?.type.events[eventId];
|
|
166
|
+
let limits;
|
|
167
|
+
if (event === undefined) {
|
|
168
|
+
// We still need to authorize the user for access even though this path doesn't resolve. Spec is not
|
|
169
|
+
// explicit on what privilege level we should require as normally that information comes from the resolved
|
|
170
|
+
// event. So attempt to resolve via the active model
|
|
171
|
+
const modelEvent = this.node.matter
|
|
172
|
+
.member(path.clusterId, [ElementTag.Cluster])
|
|
173
|
+
?.member(path.eventId, [ElementTag.Event]);
|
|
174
|
+
|
|
175
|
+
if (modelEvent) {
|
|
176
|
+
// OK cluster doesn't exist at that location, but we do understand semantically, so use limits from the
|
|
177
|
+
// model
|
|
178
|
+
limits = AccessControl(modelEvent as EventModel).limits;
|
|
179
|
+
} else {
|
|
180
|
+
// We've got no idea. This effectively falls back to "view" privilege
|
|
181
|
+
limits = FallbackLimits;
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
limits = event.limits;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Validate access. Order here prescribed by 1.4 core spec 8.4.3.2
|
|
188
|
+
// We need some fallback location if cluster is not defined
|
|
189
|
+
const location = {
|
|
190
|
+
...(cluster?.location ?? {
|
|
191
|
+
path: DataModelPath.none,
|
|
192
|
+
endpoint: endpointId,
|
|
193
|
+
cluster: clusterId,
|
|
194
|
+
}),
|
|
195
|
+
owningFabric: this.session.fabric,
|
|
196
|
+
};
|
|
197
|
+
const permission = this.session.authorityAt(limits.readLevel, location);
|
|
198
|
+
switch (permission) {
|
|
199
|
+
case AccessControl.Authority.Granted:
|
|
200
|
+
break;
|
|
201
|
+
|
|
202
|
+
case AccessControl.Authority.Unauthorized:
|
|
203
|
+
return this.#asStatus(path, Status.UnsupportedAccess);
|
|
204
|
+
|
|
205
|
+
case AccessControl.Authority.Restricted:
|
|
206
|
+
return this.#asStatus(path, Status.AccessRestricted);
|
|
207
|
+
|
|
208
|
+
default:
|
|
209
|
+
throw new InternalError(`Unsupported authorization state ${permission}`);
|
|
210
|
+
}
|
|
211
|
+
if (endpoint === undefined) {
|
|
212
|
+
return this.#asStatus(path, Status.UnsupportedEndpoint);
|
|
213
|
+
}
|
|
214
|
+
if (cluster === undefined) {
|
|
215
|
+
return this.#asStatus(path, Status.UnsupportedCluster);
|
|
216
|
+
}
|
|
217
|
+
if (event === undefined || !cluster.type.events[event.id]) {
|
|
218
|
+
return this.#asStatus(path, Status.UnsupportedEvent);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (this.#currentEndpoint !== endpoint) {
|
|
222
|
+
this.#currentEndpoint = endpoint;
|
|
223
|
+
this.#currentCluster = cluster;
|
|
224
|
+
} else if (this.#currentCluster !== cluster) {
|
|
225
|
+
this.#currentCluster = cluster;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Register allowed event-path
|
|
229
|
+
this.#registerEventPath(path);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Starts new chunk or adds to current chunk all values from {@link endpoint} selected by {@link path}.
|
|
234
|
+
*
|
|
235
|
+
* Emits previous chunk if it exists and was not for this endpoint. This means that our chunk size is one endpoint
|
|
236
|
+
* worth of data, except for the initial error chunk if there are path errors.
|
|
237
|
+
*
|
|
238
|
+
* TODO - skip endpoints for which subject is unauthorized
|
|
239
|
+
*/
|
|
240
|
+
#addEndpointForWildcard(endpoint: EndpointProtocol, path: EventPath) {
|
|
241
|
+
if (this.#currentEndpoint !== endpoint) {
|
|
242
|
+
this.#currentEndpoint = endpoint;
|
|
243
|
+
this.#currentCluster = undefined;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const { clusterId } = path;
|
|
247
|
+
if (clusterId === undefined) {
|
|
248
|
+
for (const cluster of endpoint) {
|
|
249
|
+
this.#addClusterForWildcard(cluster, { ...path, endpointId: endpoint.id });
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
const cluster = endpoint[clusterId];
|
|
253
|
+
if (cluster !== undefined) {
|
|
254
|
+
this.#addClusterForWildcard(cluster, { ...path, endpointId: endpoint.id });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Read values from a specific {@link cluster} for a wildcard path.
|
|
261
|
+
*
|
|
262
|
+
* Depends on state initialized by {@link #addEndpointForWildcard}.
|
|
263
|
+
*
|
|
264
|
+
* TODO - skip endpoints for which subject is unauthorized as optimization
|
|
265
|
+
*/
|
|
266
|
+
#addClusterForWildcard(cluster: ClusterProtocol, path: EventPath) {
|
|
267
|
+
if (this.#currentCluster !== cluster) {
|
|
268
|
+
this.#currentCluster = cluster;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const { eventId } = path;
|
|
272
|
+
if (eventId === undefined) {
|
|
273
|
+
for (const event of cluster.type.events) {
|
|
274
|
+
this.#addEventForWildcard(event, {
|
|
275
|
+
...path,
|
|
276
|
+
clusterId: cluster.type.id,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
const event = cluster.type.events[eventId];
|
|
281
|
+
if (event !== undefined) {
|
|
282
|
+
this.#addEventForWildcard(event, {
|
|
283
|
+
...path,
|
|
284
|
+
clusterId: cluster.type.id,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Read values from a specific {@link event} for a wildcard path.
|
|
292
|
+
*
|
|
293
|
+
* Depends on state initialized by {@link #addClusterForWildcard}.
|
|
294
|
+
*/
|
|
295
|
+
#addEventForWildcard(event: EventTypeProtocol, path: EventPath) {
|
|
296
|
+
if (!this.#guardedCurrentCluster.type.events[event.id]) {
|
|
297
|
+
return; // EVent is not active, so ignore
|
|
298
|
+
}
|
|
299
|
+
if (
|
|
300
|
+
this.session.authorityAt(event.limits.readLevel, this.#guardedCurrentCluster.location) !==
|
|
301
|
+
AccessControl.Authority.Granted
|
|
302
|
+
) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
this.#registerEventPath({ ...path, eventId: event.id } as ReadResult.ConcreteEventPath);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#createEventKey(path: ReadResult.ConcreteEventPath) {
|
|
310
|
+
const { endpointId, clusterId, eventId } = path;
|
|
311
|
+
return `${endpointId}:${clusterId}:${eventId}`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#registerEventPath(path: ReadResult.ConcreteEventPath) {
|
|
315
|
+
const { eventId } = path;
|
|
316
|
+
this.#allowedEventPaths.set(
|
|
317
|
+
this.#createEventKey({
|
|
318
|
+
...path,
|
|
319
|
+
endpointId: this.#guardedCurrentEndpoint.id,
|
|
320
|
+
clusterId: this.#guardedCurrentCluster.type.id,
|
|
321
|
+
eventId,
|
|
322
|
+
}),
|
|
323
|
+
this.#guardedCurrentCluster.type.events[eventId]!.tlv,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async *#readAllowedEvents() {
|
|
328
|
+
for await (const event of this.node.eventHandler.get(this.#eventMinVersion)) {
|
|
329
|
+
const tlv = this.#allowedEventPaths.get(this.#createEventKey(event));
|
|
330
|
+
if (tlv === undefined) {
|
|
331
|
+
// This event is not in the allowed list, so skip it
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
// Filter out if we need to do fabric filtering and the event is not for the current fabric
|
|
335
|
+
if (this.#filteredForFabricIndex !== undefined) {
|
|
336
|
+
const { payload } = event;
|
|
337
|
+
if (!isObject(payload)) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
const { fabricIndex } = payload;
|
|
341
|
+
if (fabricIndex !== undefined && fabricIndex !== this.#filteredForFabricIndex) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
yield this.#asValue(event, tlv);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Add a status value.
|
|
351
|
+
*/
|
|
352
|
+
#asStatus(path: ReadResult.ConcreteEventPath, status: Status) {
|
|
353
|
+
logger.debug(`Error reading event ${this.node.inspectPath(path)}: Status=${StatusCode[status]}(${status})`);
|
|
354
|
+
|
|
355
|
+
const report: ReadResult.GlobalEventStatus = {
|
|
356
|
+
kind: "event-status",
|
|
357
|
+
path,
|
|
358
|
+
status,
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
return report;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Add event values as separate entries to the result.
|
|
366
|
+
*/
|
|
367
|
+
#asValue(event: NumberedOccurrence, tlv: TlvSchema<unknown>) {
|
|
368
|
+
const { number, epochTimestamp: timestamp, priority, payload: value, endpointId, clusterId, eventId } = event;
|
|
369
|
+
const report: ReadResult.EventValue = {
|
|
370
|
+
kind: "event-value",
|
|
371
|
+
path: { endpointId, clusterId, eventId },
|
|
372
|
+
number,
|
|
373
|
+
priority,
|
|
374
|
+
timestamp,
|
|
375
|
+
value,
|
|
376
|
+
tlv,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
return report;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
@@ -15,9 +15,12 @@ import { ReadResult } from "#action/response/ReadResult.js";
|
|
|
15
15
|
import { SubscribeResult } from "#action/response/SubscribeResult.js";
|
|
16
16
|
import { WriteResult } from "#action/response/WriteResult.js";
|
|
17
17
|
import { AccessControl } from "#action/server/AccessControl.js";
|
|
18
|
-
import {
|
|
18
|
+
import { EventResponse } from "#action/server/EventResponse.js";
|
|
19
|
+
import { Logger, NotImplementedError } from "#general";
|
|
19
20
|
import { AttributeResponse } from "./AttributeResponse.js";
|
|
20
21
|
|
|
22
|
+
const logger = Logger.get("ServerInteraction");
|
|
23
|
+
|
|
21
24
|
/**
|
|
22
25
|
* Implementation of server interaction.
|
|
23
26
|
*
|
|
@@ -40,11 +43,22 @@ export class ServerInteraction<SessionT extends AccessControl.Session = AccessCo
|
|
|
40
43
|
async *read(request: Read, session: SessionT): ReadResult {
|
|
41
44
|
// TODO - validate request
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
let readInfo = "";
|
|
47
|
+
if (Read.containsAttribute(request)) {
|
|
48
|
+
const attributeReader = new AttributeResponse(this.#node, session);
|
|
49
|
+
yield* attributeReader.process(request);
|
|
50
|
+
|
|
51
|
+
const { existent, status, value } = attributeReader.counts;
|
|
52
|
+
readInfo = `${existent} matching attributes (${status ? `${status} status, ` : ""}${value ? `${value} values` : ""})`;
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
if (Read.containsEvent(request)) {
|
|
56
|
+
const eventReader = new EventResponse(this.#node, session);
|
|
57
|
+
yield* eventReader.process(request);
|
|
58
|
+
const { existent, status, value } = eventReader.counts;
|
|
59
|
+
readInfo += `${readInfo.length > 0 ? ", " : ""}${existent} matching events (${status ? `${status} status, ` : ""}${value ? `${value} values` : ""})`;
|
|
60
|
+
}
|
|
61
|
+
logger.debug(`Read request resolved to ${readInfo}`);
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
subscribe(_request: Subscribe, _session?: SessionT): SubscribeResult {
|