@matter/protocol 0.16.0-alpha.0-20251030-e9ca79f93 → 0.16.0-alpha.0-20251101-70c8d51d7
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/Interactable.d.ts +1 -0
- package/dist/cjs/action/Interactable.d.ts.map +1 -1
- package/dist/cjs/action/client/ClientInteraction.d.ts +25 -19
- package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/cjs/action/client/ClientInteraction.js +198 -94
- package/dist/cjs/action/client/ClientInteraction.js.map +2 -2
- package/dist/cjs/action/client/index.d.ts +1 -3
- package/dist/cjs/action/client/index.d.ts.map +1 -1
- package/dist/cjs/action/client/index.js +1 -3
- package/dist/cjs/action/client/index.js.map +1 -1
- package/dist/cjs/action/client/subscription/ClientSubscribe.d.ts +8 -0
- package/dist/cjs/action/client/subscription/ClientSubscribe.d.ts.map +1 -0
- package/dist/cjs/action/client/{ClientSubscription.js → subscription/ClientSubscribe.js} +3 -8
- package/dist/cjs/action/client/subscription/ClientSubscribe.js.map +6 -0
- package/dist/cjs/action/client/subscription/ClientSubscription.d.ts +38 -0
- package/dist/cjs/action/client/subscription/ClientSubscription.d.ts.map +1 -0
- package/dist/cjs/action/client/subscription/ClientSubscription.js +79 -0
- package/dist/cjs/action/client/subscription/ClientSubscription.js.map +6 -0
- package/dist/cjs/action/client/subscription/ClientSubscriptionHandler.d.ts.map +1 -0
- package/dist/cjs/action/client/{ClientSubscriptionHandler.js → subscription/ClientSubscriptionHandler.js} +5 -2
- package/dist/cjs/action/client/subscription/ClientSubscriptionHandler.js.map +6 -0
- package/dist/{esm/action/client → cjs/action/client/subscription}/ClientSubscriptions.d.ts +15 -8
- package/dist/cjs/action/client/subscription/ClientSubscriptions.d.ts.map +1 -0
- package/dist/cjs/action/client/subscription/ClientSubscriptions.js +133 -0
- package/dist/cjs/action/client/subscription/ClientSubscriptions.js.map +6 -0
- package/dist/cjs/action/client/subscription/PeerSubscription.d.ts +27 -0
- package/dist/cjs/action/client/subscription/PeerSubscription.d.ts.map +1 -0
- package/dist/cjs/action/client/subscription/PeerSubscription.js +57 -0
- package/dist/cjs/action/client/subscription/PeerSubscription.js.map +6 -0
- package/dist/cjs/action/client/subscription/SustainedSubscription.d.ts +57 -0
- package/dist/cjs/action/client/subscription/SustainedSubscription.d.ts.map +1 -0
- package/dist/cjs/action/client/subscription/SustainedSubscription.js +143 -0
- package/dist/cjs/action/client/subscription/SustainedSubscription.js.map +6 -0
- package/dist/cjs/action/client/subscription/index.d.ts +12 -0
- package/dist/cjs/action/client/subscription/index.d.ts.map +1 -0
- package/dist/cjs/action/client/subscription/index.js +29 -0
- package/dist/cjs/action/client/subscription/index.js.map +6 -0
- package/dist/cjs/action/errors.d.ts +7 -2
- package/dist/cjs/action/errors.d.ts.map +1 -1
- package/dist/cjs/action/errors.js +6 -3
- package/dist/cjs/action/errors.js.map +1 -1
- package/dist/cjs/action/request/Subscribe.d.ts +2 -2
- package/dist/cjs/action/request/Subscribe.d.ts.map +1 -1
- package/dist/cjs/action/request/Subscribe.js.map +1 -1
- package/dist/cjs/action/response/ReadResult.d.ts +1 -1
- package/dist/cjs/action/response/ReadResult.d.ts.map +1 -1
- package/dist/cjs/action/response/SubscribeResult.d.ts +2 -1
- package/dist/cjs/action/response/SubscribeResult.d.ts.map +1 -1
- package/dist/cjs/action/server/ServerInteraction.d.ts +0 -1
- package/dist/cjs/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/cjs/action/server/ServerInteraction.js +0 -3
- package/dist/cjs/action/server/ServerInteraction.js.map +1 -1
- package/dist/cjs/bdx/flow/InboundFlow.js +1 -1
- package/dist/cjs/bdx/flow/InboundFlow.js.map +1 -1
- package/dist/cjs/interaction/SubscriptionClient.d.ts +3 -3
- package/dist/cjs/interaction/SubscriptionClient.d.ts.map +1 -1
- package/dist/cjs/interaction/SubscriptionClient.js +0 -7
- package/dist/cjs/interaction/SubscriptionClient.js.map +1 -1
- package/dist/cjs/peer/PeerSet.d.ts +1 -1
- package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
- package/dist/cjs/protocol/MessageExchange.js +1 -1
- package/dist/cjs/protocol/MessageExchange.js.map +1 -1
- package/dist/esm/action/Interactable.d.ts +1 -0
- package/dist/esm/action/Interactable.d.ts.map +1 -1
- package/dist/esm/action/client/ClientInteraction.d.ts +25 -19
- package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/esm/action/client/ClientInteraction.js +201 -95
- package/dist/esm/action/client/ClientInteraction.js.map +2 -2
- package/dist/esm/action/client/index.d.ts +1 -3
- package/dist/esm/action/client/index.d.ts.map +1 -1
- package/dist/esm/action/client/index.js +1 -3
- package/dist/esm/action/client/index.js.map +1 -1
- package/dist/esm/action/client/subscription/ClientSubscribe.d.ts +8 -0
- package/dist/esm/action/client/subscription/ClientSubscribe.d.ts.map +1 -0
- package/dist/esm/action/client/subscription/ClientSubscribe.js +1 -0
- package/dist/esm/action/client/subscription/ClientSubscribe.js.map +6 -0
- package/dist/esm/action/client/subscription/ClientSubscription.d.ts +38 -0
- package/dist/esm/action/client/subscription/ClientSubscription.d.ts.map +1 -0
- package/dist/esm/action/client/subscription/ClientSubscription.js +59 -0
- package/dist/esm/action/client/subscription/ClientSubscription.js.map +6 -0
- package/dist/esm/action/client/subscription/ClientSubscriptionHandler.d.ts.map +1 -0
- package/dist/esm/action/client/{ClientSubscriptionHandler.js → subscription/ClientSubscriptionHandler.js} +5 -2
- package/dist/esm/action/client/subscription/ClientSubscriptionHandler.js.map +6 -0
- package/dist/{cjs/action/client → esm/action/client/subscription}/ClientSubscriptions.d.ts +15 -8
- package/dist/esm/action/client/subscription/ClientSubscriptions.d.ts.map +1 -0
- package/dist/esm/action/client/subscription/ClientSubscriptions.js +113 -0
- package/dist/esm/action/client/subscription/ClientSubscriptions.js.map +6 -0
- package/dist/esm/action/client/subscription/PeerSubscription.d.ts +27 -0
- package/dist/esm/action/client/subscription/PeerSubscription.d.ts.map +1 -0
- package/dist/esm/action/client/subscription/PeerSubscription.js +37 -0
- package/dist/esm/action/client/subscription/PeerSubscription.js.map +6 -0
- package/dist/esm/action/client/subscription/SustainedSubscription.d.ts +57 -0
- package/dist/esm/action/client/subscription/SustainedSubscription.d.ts.map +1 -0
- package/dist/esm/action/client/subscription/SustainedSubscription.js +133 -0
- package/dist/esm/action/client/subscription/SustainedSubscription.js.map +6 -0
- package/dist/esm/action/client/subscription/index.d.ts +12 -0
- package/dist/esm/action/client/subscription/index.d.ts.map +1 -0
- package/dist/esm/action/client/subscription/index.js +12 -0
- package/dist/esm/action/client/subscription/index.js.map +6 -0
- package/dist/esm/action/errors.d.ts +7 -2
- package/dist/esm/action/errors.d.ts.map +1 -1
- package/dist/esm/action/errors.js +6 -3
- package/dist/esm/action/errors.js.map +1 -1
- package/dist/esm/action/request/Subscribe.d.ts +2 -2
- package/dist/esm/action/request/Subscribe.d.ts.map +1 -1
- package/dist/esm/action/request/Subscribe.js.map +1 -1
- package/dist/esm/action/response/ReadResult.d.ts +1 -1
- package/dist/esm/action/response/ReadResult.d.ts.map +1 -1
- package/dist/esm/action/response/SubscribeResult.d.ts +2 -1
- package/dist/esm/action/response/SubscribeResult.d.ts.map +1 -1
- package/dist/esm/action/server/ServerInteraction.d.ts +0 -1
- package/dist/esm/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/esm/action/server/ServerInteraction.js +0 -3
- package/dist/esm/action/server/ServerInteraction.js.map +1 -1
- package/dist/esm/bdx/flow/InboundFlow.js +1 -1
- package/dist/esm/bdx/flow/InboundFlow.js.map +1 -1
- package/dist/esm/interaction/SubscriptionClient.d.ts +3 -3
- package/dist/esm/interaction/SubscriptionClient.d.ts.map +1 -1
- package/dist/esm/interaction/SubscriptionClient.js +1 -8
- package/dist/esm/interaction/SubscriptionClient.js.map +1 -1
- package/dist/esm/peer/PeerSet.d.ts +1 -1
- package/dist/esm/peer/PeerSet.d.ts.map +1 -1
- package/dist/esm/protocol/MessageExchange.js +1 -1
- package/dist/esm/protocol/MessageExchange.js.map +1 -1
- package/package.json +6 -6
- package/src/action/Interactable.ts +1 -0
- package/src/action/client/ClientInteraction.ts +273 -235
- package/src/action/client/index.ts +1 -3
- package/src/action/client/subscription/ClientSubscribe.ts +8 -0
- package/src/action/client/subscription/ClientSubscription.ts +88 -0
- package/src/action/client/{ClientSubscriptionHandler.ts → subscription/ClientSubscriptionHandler.ts} +5 -2
- package/src/action/client/subscription/ClientSubscriptions.ts +150 -0
- package/src/action/client/subscription/PeerSubscription.ts +51 -0
- package/src/action/client/subscription/SustainedSubscription.ts +199 -0
- package/src/action/client/subscription/index.ts +12 -0
- package/src/action/errors.ts +11 -6
- package/src/action/request/Subscribe.ts +2 -2
- package/src/action/response/ReadResult.ts +1 -1
- package/src/action/response/SubscribeResult.ts +2 -1
- package/src/action/server/ServerInteraction.ts +0 -5
- package/src/bdx/flow/InboundFlow.ts +1 -1
- package/src/interaction/SubscriptionClient.ts +4 -9
- package/src/protocol/MessageExchange.ts +1 -1
- package/dist/cjs/action/client/ClientSubscription.d.ts +0 -18
- package/dist/cjs/action/client/ClientSubscription.d.ts.map +0 -1
- package/dist/cjs/action/client/ClientSubscription.js.map +0 -6
- package/dist/cjs/action/client/ClientSubscriptionHandler.d.ts.map +0 -1
- package/dist/cjs/action/client/ClientSubscriptionHandler.js.map +0 -6
- package/dist/cjs/action/client/ClientSubscriptions.d.ts.map +0 -1
- package/dist/cjs/action/client/ClientSubscriptions.js +0 -145
- package/dist/cjs/action/client/ClientSubscriptions.js.map +0 -6
- package/dist/esm/action/client/ClientSubscription.d.ts +0 -18
- package/dist/esm/action/client/ClientSubscription.d.ts.map +0 -1
- package/dist/esm/action/client/ClientSubscription.js +0 -6
- package/dist/esm/action/client/ClientSubscription.js.map +0 -6
- package/dist/esm/action/client/ClientSubscriptionHandler.d.ts.map +0 -1
- package/dist/esm/action/client/ClientSubscriptionHandler.js.map +0 -6
- package/dist/esm/action/client/ClientSubscriptions.d.ts.map +0 -1
- package/dist/esm/action/client/ClientSubscriptions.js +0 -135
- package/dist/esm/action/client/ClientSubscriptions.js.map +0 -6
- package/src/action/client/ClientSubscription.ts +0 -19
- package/src/action/client/ClientSubscriptions.ts +0 -178
- /package/dist/cjs/action/client/{ClientSubscriptionHandler.d.ts → subscription/ClientSubscriptionHandler.d.ts} +0 -0
- /package/dist/esm/action/client/{ClientSubscriptionHandler.d.ts → subscription/ClientSubscriptionHandler.d.ts} +0 -0
|
@@ -12,33 +12,37 @@ import { Subscribe } from "#action/request/Subscribe.js";
|
|
|
12
12
|
import { Write } from "#action/request/Write.js";
|
|
13
13
|
import { DecodedInvokeResult, InvokeResult } from "#action/response/InvokeResult.js";
|
|
14
14
|
import { ReadResult } from "#action/response/ReadResult.js";
|
|
15
|
-
import { SubscribeResult } from "#action/response/SubscribeResult.js";
|
|
16
15
|
import { WriteResult } from "#action/response/WriteResult.js";
|
|
17
16
|
import {
|
|
17
|
+
Abort,
|
|
18
18
|
BasicSet,
|
|
19
19
|
Diagnostic,
|
|
20
20
|
Duration,
|
|
21
|
+
Entropy,
|
|
21
22
|
Environment,
|
|
22
|
-
Environmental,
|
|
23
23
|
ImplementationError,
|
|
24
24
|
isObject,
|
|
25
25
|
Logger,
|
|
26
|
-
|
|
26
|
+
RetrySchedule,
|
|
27
27
|
Seconds,
|
|
28
28
|
} from "#general";
|
|
29
29
|
import { InteractionClientMessenger, MessageType } from "#interaction/InteractionMessenger.js";
|
|
30
|
-
import { InteractionQueue } from "#peer/InteractionQueue.js";
|
|
31
30
|
import { ExchangeProvider } from "#protocol/ExchangeProvider.js";
|
|
31
|
+
import { SecureSession } from "#session/SecureSession.js";
|
|
32
32
|
import { Status, TlvNoResponse, TlvSubscribeResponse } from "#types";
|
|
33
|
-
import { ClientSubscriptions } from "./ClientSubscriptions.js";
|
|
34
33
|
import { InputChunk } from "./InputChunk.js";
|
|
34
|
+
import { ClientSubscribe } from "./subscription/ClientSubscribe.js";
|
|
35
|
+
import { ClientSubscription } from "./subscription/ClientSubscription.js";
|
|
36
|
+
import { ClientSubscriptions } from "./subscription/ClientSubscriptions.js";
|
|
37
|
+
import { PeerSubscription } from "./subscription/PeerSubscription.js";
|
|
38
|
+
import { SustainedSubscription } from "./subscription/SustainedSubscription.js";
|
|
35
39
|
|
|
36
40
|
const logger = Logger.get("ClientInteraction");
|
|
37
41
|
|
|
38
42
|
export interface ClientInteractionContext {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
environment: Environment;
|
|
44
|
+
abort?: Abort.Signal;
|
|
45
|
+
sustainRetries?: RetrySchedule.Configuration;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
export const DEFAULT_MIN_INTERVAL_FLOOR = Seconds(1);
|
|
@@ -54,18 +58,22 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
54
58
|
{
|
|
55
59
|
readonly #exchanges: ExchangeProvider;
|
|
56
60
|
readonly #subscriptions: ClientSubscriptions;
|
|
57
|
-
readonly #queue?: PromiseQueue;
|
|
58
61
|
readonly #interactions = new BasicSet<Read | Write | Invoke | Subscribe>();
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.#
|
|
64
|
-
this.#
|
|
62
|
+
readonly #abort;
|
|
63
|
+
readonly #sustainRetries: RetrySchedule;
|
|
64
|
+
|
|
65
|
+
constructor({ environment, abort, sustainRetries }: ClientInteractionContext) {
|
|
66
|
+
this.#exchanges = environment.get(ExchangeProvider);
|
|
67
|
+
this.#subscriptions = environment.get(ClientSubscriptions);
|
|
68
|
+
this.#abort = Abort.subtask(abort);
|
|
69
|
+
this.#sustainRetries = new RetrySchedule(
|
|
70
|
+
environment.get(Entropy),
|
|
71
|
+
RetrySchedule.Configuration(SustainedSubscription.DefaultRetrySchedule, sustainRetries),
|
|
72
|
+
);
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
async close() {
|
|
68
|
-
this.#
|
|
76
|
+
this.#abort();
|
|
69
77
|
|
|
70
78
|
while (this.#interactions.size) {
|
|
71
79
|
await this.#interactions.deleted;
|
|
@@ -76,21 +84,10 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
76
84
|
return this.#subscriptions;
|
|
77
85
|
}
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
static [Environmental.create](env: Environment) {
|
|
84
|
-
const instance = new ClientInteraction({
|
|
85
|
-
exchanges: env.get(ExchangeProvider),
|
|
86
|
-
subscriptions: env.get(ClientSubscriptions),
|
|
87
|
-
queue: env.get(InteractionQueue),
|
|
88
|
-
});
|
|
89
|
-
env.set(ClientInteraction, instance);
|
|
90
|
-
return instance;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async *read(request: Read, _session?: SessionT): ReadResult {
|
|
87
|
+
/**
|
|
88
|
+
* Read attributes and events.
|
|
89
|
+
*/
|
|
90
|
+
async *read(request: Read, session?: SessionT): ReadResult {
|
|
94
91
|
const readPathsCount = (request.attributeRequests?.length ?? 0) + (request.eventRequests?.length ?? 0);
|
|
95
92
|
if (readPathsCount > 9) {
|
|
96
93
|
logger.debug(
|
|
@@ -98,215 +95,209 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
98
95
|
);
|
|
99
96
|
}
|
|
100
97
|
|
|
101
|
-
this.#begin(request);
|
|
102
|
-
|
|
103
|
-
let messenger: undefined | InteractionClientMessenger;
|
|
104
|
-
try {
|
|
105
|
-
messenger = await InteractionClientMessenger.create(this.#exchanges);
|
|
106
|
-
|
|
107
|
-
logger.debug("Read »", messenger.exchange.via, request);
|
|
108
|
-
await messenger.sendReadRequest(request);
|
|
98
|
+
await using context = await this.#begin(request, session);
|
|
99
|
+
const { checkAbort, messenger } = context;
|
|
109
100
|
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
logger.debug("Read »", messenger.exchange.via, request);
|
|
102
|
+
await messenger.sendReadRequest(request);
|
|
103
|
+
checkAbort();
|
|
112
104
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
eventReportCount += report.eventReports?.length ?? 0;
|
|
116
|
-
yield InputChunk(report);
|
|
117
|
-
}
|
|
105
|
+
let attributeReportCount = 0;
|
|
106
|
+
let eventReportCount = 0;
|
|
118
107
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
: Diagnostic.dict({ attributes: attributeReportCount, events: eventReportCount }),
|
|
126
|
-
),
|
|
127
|
-
);
|
|
128
|
-
} finally {
|
|
129
|
-
await messenger?.close();
|
|
130
|
-
this.#end(request);
|
|
108
|
+
for await (const report of messenger.readDataReports()) {
|
|
109
|
+
checkAbort();
|
|
110
|
+
attributeReportCount += report.attributeReports?.length ?? 0;
|
|
111
|
+
eventReportCount += report.eventReports?.length ?? 0;
|
|
112
|
+
yield InputChunk(report);
|
|
113
|
+
checkAbort();
|
|
131
114
|
}
|
|
115
|
+
|
|
116
|
+
logger.debug(
|
|
117
|
+
"Read «",
|
|
118
|
+
messenger.exchange.via,
|
|
119
|
+
Diagnostic.weak(
|
|
120
|
+
attributeReportCount + eventReportCount === 0
|
|
121
|
+
? "(empty)"
|
|
122
|
+
: Diagnostic.dict({ attributes: attributeReportCount, events: eventReportCount }),
|
|
123
|
+
),
|
|
124
|
+
);
|
|
132
125
|
}
|
|
133
126
|
|
|
134
127
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
128
|
+
* Update node attributes.
|
|
129
|
+
*
|
|
130
|
+
* Writes with the Matter protocol are generally not atomic, so this method only throws if the entire action fails.
|
|
131
|
+
* You must check each {@link WriteResult.AttributeStatus} to determine whether individual updates failed.
|
|
138
132
|
*/
|
|
139
|
-
async write<T extends Write>(request: T,
|
|
140
|
-
this.#begin(request);
|
|
141
|
-
|
|
142
|
-
let messenger: undefined | InteractionClientMessenger;
|
|
143
|
-
try {
|
|
144
|
-
messenger = await InteractionClientMessenger.create(this.#exchanges);
|
|
145
|
-
|
|
146
|
-
if (request.timedRequest) {
|
|
147
|
-
await messenger.sendTimedRequest(request.timeout ?? DEFAULT_TIMED_REQUEST_TIMEOUT);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
logger.info("Write »", messenger.exchange.via, request);
|
|
151
|
-
|
|
152
|
-
const response = await messenger.sendWriteCommand(request);
|
|
153
|
-
if (request.suppressResponse) {
|
|
154
|
-
return undefined as Awaited<WriteResult<T>>;
|
|
155
|
-
}
|
|
156
|
-
if (!response || !response.writeResponses?.length) {
|
|
157
|
-
return [] as Awaited<WriteResult<T>>;
|
|
158
|
-
}
|
|
133
|
+
async write<T extends Write>(request: T, session?: SessionT): WriteResult<T> {
|
|
134
|
+
await using context = await this.#begin(request, session);
|
|
135
|
+
const { checkAbort, messenger } = context;
|
|
159
136
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
path: { nodeId, endpointId, clusterId, attributeId, listIndex },
|
|
165
|
-
status: { status, clusterStatus },
|
|
166
|
-
}) => {
|
|
167
|
-
if (status === Status.Success) {
|
|
168
|
-
successCount++;
|
|
169
|
-
} else {
|
|
170
|
-
failureCount++;
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
kind: "attr-status",
|
|
174
|
-
path: {
|
|
175
|
-
nodeId,
|
|
176
|
-
endpointId: endpointId!,
|
|
177
|
-
clusterId: clusterId!,
|
|
178
|
-
attributeId: attributeId!,
|
|
179
|
-
listIndex,
|
|
180
|
-
},
|
|
181
|
-
status,
|
|
182
|
-
clusterStatus,
|
|
183
|
-
};
|
|
184
|
-
},
|
|
185
|
-
) as Awaited<WriteResult<T>>;
|
|
137
|
+
if (request.timedRequest) {
|
|
138
|
+
await messenger.sendTimedRequest(request.timeout ?? DEFAULT_TIMED_REQUEST_TIMEOUT);
|
|
139
|
+
checkAbort();
|
|
140
|
+
}
|
|
186
141
|
|
|
187
|
-
|
|
188
|
-
"Write «",
|
|
189
|
-
messenger.exchange.via,
|
|
190
|
-
Diagnostic.weak(
|
|
191
|
-
successCount + failureCount === 0
|
|
192
|
-
? "(empty)"
|
|
193
|
-
: Diagnostic.dict({ success: successCount, failure: failureCount }),
|
|
194
|
-
),
|
|
195
|
-
);
|
|
142
|
+
logger.info("Write »", messenger.exchange.via, request);
|
|
196
143
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
144
|
+
const response = await messenger.sendWriteCommand(request);
|
|
145
|
+
checkAbort();
|
|
146
|
+
if (request.suppressResponse) {
|
|
147
|
+
return undefined as Awaited<WriteResult<T>>;
|
|
148
|
+
}
|
|
149
|
+
if (!response || !response.writeResponses?.length) {
|
|
150
|
+
return [] as Awaited<WriteResult<T>>;
|
|
201
151
|
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
async *invoke(request: ClientInvoke, _session?: SessionT): DecodedInvokeResult {
|
|
205
|
-
this.#begin(request);
|
|
206
152
|
|
|
207
|
-
let
|
|
208
|
-
|
|
209
|
-
|
|
153
|
+
let successCount = 0;
|
|
154
|
+
let failureCount = 0;
|
|
155
|
+
const result = response.writeResponses.map(
|
|
156
|
+
({
|
|
157
|
+
path: { nodeId, endpointId, clusterId, attributeId, listIndex },
|
|
158
|
+
status: { status, clusterStatus },
|
|
159
|
+
}) => {
|
|
160
|
+
if (status === Status.Success) {
|
|
161
|
+
successCount++;
|
|
162
|
+
} else {
|
|
163
|
+
failureCount++;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
kind: "attr-status",
|
|
167
|
+
path: {
|
|
168
|
+
nodeId,
|
|
169
|
+
endpointId: endpointId!,
|
|
170
|
+
clusterId: clusterId!,
|
|
171
|
+
attributeId: attributeId!,
|
|
172
|
+
listIndex,
|
|
173
|
+
},
|
|
174
|
+
status,
|
|
175
|
+
clusterStatus,
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
) as Awaited<WriteResult<T>>;
|
|
179
|
+
|
|
180
|
+
logger.info(
|
|
181
|
+
"Write «",
|
|
182
|
+
messenger.exchange.via,
|
|
183
|
+
Diagnostic.weak(
|
|
184
|
+
successCount + failureCount === 0
|
|
185
|
+
? "(empty)"
|
|
186
|
+
: Diagnostic.dict({ success: successCount, failure: failureCount }),
|
|
187
|
+
),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
210
192
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Invoke one or more commands.
|
|
195
|
+
*/
|
|
196
|
+
async *invoke(request: ClientInvoke, session?: SessionT): DecodedInvokeResult {
|
|
197
|
+
await using context = await this.#begin(request, session);
|
|
198
|
+
const { checkAbort, messenger } = context;
|
|
214
199
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
request,
|
|
220
|
-
);
|
|
200
|
+
if (request.timedRequest) {
|
|
201
|
+
await messenger.sendTimedRequest(request.timeout ?? DEFAULT_TIMED_REQUEST_TIMEOUT);
|
|
202
|
+
checkAbort();
|
|
203
|
+
}
|
|
221
204
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const data =
|
|
254
|
-
commandFields === undefined ? undefined : responseSchema.decodeTlv(commandFields);
|
|
255
|
-
|
|
256
|
-
logger.info(
|
|
257
|
-
"Invoke «",
|
|
258
|
-
messenger!.exchange.via,
|
|
259
|
-
Diagnostic.strong(resolvePathForSpecifier(cmd)),
|
|
260
|
-
isObject(data) ? Diagnostic.dict(data) : Diagnostic.weak("(no payload)"),
|
|
205
|
+
logger.info(
|
|
206
|
+
"Invoke »",
|
|
207
|
+
messenger.exchange.via,
|
|
208
|
+
Diagnostic.asFlags({ suppressResponse: request.suppressResponse, timed: request.timedRequest }),
|
|
209
|
+
request,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const { expectedProcessingTime, useExtendedFailSafeMessageResponseTimeout } = request;
|
|
213
|
+
const result = await messenger.sendInvokeCommand(
|
|
214
|
+
request,
|
|
215
|
+
expectedProcessingTime ??
|
|
216
|
+
(useExtendedFailSafeMessageResponseTimeout
|
|
217
|
+
? DEFAULT_MINIMUM_RESPONSE_TIMEOUT_WITH_FAILSAFE
|
|
218
|
+
: undefined),
|
|
219
|
+
);
|
|
220
|
+
checkAbort();
|
|
221
|
+
if (!request.suppressResponse) {
|
|
222
|
+
if (result && result.invokeResponses?.length) {
|
|
223
|
+
const chunk: InvokeResult.Chunk = result.invokeResponses
|
|
224
|
+
.map(response => {
|
|
225
|
+
if (response.command !== undefined) {
|
|
226
|
+
const {
|
|
227
|
+
commandPath: { endpointId, clusterId, commandId },
|
|
228
|
+
commandRef,
|
|
229
|
+
commandFields,
|
|
230
|
+
} = response.command;
|
|
231
|
+
const cmd = request.commands.get(commandRef);
|
|
232
|
+
if (!cmd) {
|
|
233
|
+
throw new ImplementationError(
|
|
234
|
+
`No response schema found for commandRef ${commandRef} (endpoint ${endpointId}, cluster ${clusterId}, command ${commandId})`,
|
|
261
235
|
);
|
|
262
|
-
|
|
263
|
-
const res: InvokeResult.DecodedCommandResponse = {
|
|
264
|
-
kind: "cmd-response",
|
|
265
|
-
path: {
|
|
266
|
-
endpointId: endpointId!,
|
|
267
|
-
clusterId,
|
|
268
|
-
commandId,
|
|
269
|
-
},
|
|
270
|
-
commandRef,
|
|
271
|
-
data,
|
|
272
|
-
};
|
|
273
|
-
return res;
|
|
274
|
-
} else if (response.status !== undefined) {
|
|
275
|
-
const {
|
|
276
|
-
commandPath: { endpointId, clusterId, commandId },
|
|
277
|
-
commandRef,
|
|
278
|
-
status: { status, clusterStatus },
|
|
279
|
-
} = response.status;
|
|
280
|
-
const res: InvokeResult.CommandStatus = {
|
|
281
|
-
kind: "cmd-status",
|
|
282
|
-
path: {
|
|
283
|
-
endpointId: endpointId!,
|
|
284
|
-
clusterId: clusterId,
|
|
285
|
-
commandId: commandId,
|
|
286
|
-
},
|
|
287
|
-
commandRef,
|
|
288
|
-
status,
|
|
289
|
-
clusterStatus,
|
|
290
|
-
};
|
|
291
|
-
return res;
|
|
292
|
-
} else {
|
|
293
|
-
// Should not happen but if we ignore the response?
|
|
294
|
-
return undefined;
|
|
295
236
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
237
|
+
const responseSchema = Invoke.commandOf(cmd).responseSchema;
|
|
238
|
+
if (commandFields === undefined && responseSchema !== TlvNoResponse) {
|
|
239
|
+
throw new ImplementationError(
|
|
240
|
+
`No command fields found for commandRef ${commandRef} (endpoint ${endpointId}, cluster ${clusterId}, command ${commandId})`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const data =
|
|
245
|
+
commandFields === undefined ? undefined : responseSchema.decodeTlv(commandFields);
|
|
246
|
+
|
|
247
|
+
logger.info(
|
|
248
|
+
"Invoke «",
|
|
249
|
+
messenger.exchange.via,
|
|
250
|
+
Diagnostic.strong(resolvePathForSpecifier(cmd)),
|
|
251
|
+
isObject(data) ? Diagnostic.dict(data) : Diagnostic.weak("(no payload)"),
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const res: InvokeResult.DecodedCommandResponse = {
|
|
255
|
+
kind: "cmd-response",
|
|
256
|
+
path: {
|
|
257
|
+
endpointId: endpointId!,
|
|
258
|
+
clusterId,
|
|
259
|
+
commandId,
|
|
260
|
+
},
|
|
261
|
+
commandRef,
|
|
262
|
+
data,
|
|
263
|
+
};
|
|
264
|
+
return res;
|
|
265
|
+
} else if (response.status !== undefined) {
|
|
266
|
+
const {
|
|
267
|
+
commandPath: { endpointId, clusterId, commandId },
|
|
268
|
+
commandRef,
|
|
269
|
+
status: { status, clusterStatus },
|
|
270
|
+
} = response.status;
|
|
271
|
+
const res: InvokeResult.CommandStatus = {
|
|
272
|
+
kind: "cmd-status",
|
|
273
|
+
path: {
|
|
274
|
+
endpointId: endpointId!,
|
|
275
|
+
clusterId: clusterId,
|
|
276
|
+
commandId: commandId,
|
|
277
|
+
},
|
|
278
|
+
commandRef,
|
|
279
|
+
status,
|
|
280
|
+
clusterStatus,
|
|
281
|
+
};
|
|
282
|
+
return res;
|
|
283
|
+
} else {
|
|
284
|
+
// Should not happen but if we ignore the response?
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
.filter(r => r !== undefined);
|
|
289
|
+
yield chunk;
|
|
290
|
+
} else {
|
|
291
|
+
yield [];
|
|
302
292
|
}
|
|
303
|
-
|
|
304
|
-
await messenger?.close();
|
|
305
|
-
this.#end(request);
|
|
293
|
+
checkAbort();
|
|
306
294
|
}
|
|
307
295
|
}
|
|
308
296
|
|
|
309
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Subscribe to attribute values and events.
|
|
299
|
+
*/
|
|
300
|
+
async subscribe(request: ClientSubscribe, session?: SessionT) {
|
|
310
301
|
const subscriptionPathsCount = (request.attributeRequests?.length ?? 0) + (request.eventRequests?.length ?? 0);
|
|
311
302
|
if (subscriptionPathsCount > 3) {
|
|
312
303
|
logger.debug("Subscribe interactions with more then 3 paths might be not allowed by the device.");
|
|
@@ -321,11 +312,12 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
321
312
|
}
|
|
322
313
|
}
|
|
323
314
|
|
|
324
|
-
this.#
|
|
315
|
+
SecureSession.assert(this.#exchanges.session);
|
|
316
|
+
const peer = this.#exchanges.session.peerAddress;
|
|
325
317
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
messenger =
|
|
318
|
+
const subscribe = async (request: ClientSubscribe) => {
|
|
319
|
+
await using context = await this.#begin(request, session);
|
|
320
|
+
const { checkAbort, messenger } = context;
|
|
329
321
|
|
|
330
322
|
logger.info(
|
|
331
323
|
"Subscribe »",
|
|
@@ -343,8 +335,10 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
343
335
|
maxIntervalCeilingSeconds: Seconds.of(DEFAULT_MIN_INTERVAL_FLOOR), // TODO use better max fallback
|
|
344
336
|
...request,
|
|
345
337
|
});
|
|
338
|
+
checkAbort();
|
|
346
339
|
|
|
347
340
|
await this.#handleSubscriptionResponse(request, readChunks(messenger));
|
|
341
|
+
checkAbort();
|
|
348
342
|
|
|
349
343
|
const responseMessage = await messenger.nextMessage(MessageType.SubscribeResponse);
|
|
350
344
|
const response = TlvSubscribeResponse.decode(responseMessage.payload);
|
|
@@ -353,20 +347,40 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
353
347
|
"Subscription successful «",
|
|
354
348
|
messenger.exchange.via,
|
|
355
349
|
Diagnostic.dict({
|
|
356
|
-
|
|
350
|
+
id: response.subscriptionId,
|
|
357
351
|
interval: Duration.format(Seconds(response.maxInterval)),
|
|
358
352
|
}),
|
|
359
353
|
);
|
|
360
354
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
355
|
+
const subscription = new PeerSubscription({
|
|
356
|
+
request,
|
|
357
|
+
peer,
|
|
358
|
+
closed: () => this.#subscriptions.delete(subscription),
|
|
359
|
+
response,
|
|
360
|
+
abort: session?.abort,
|
|
361
|
+
});
|
|
362
|
+
this.#subscriptions.addPeer(subscription);
|
|
363
|
+
|
|
364
|
+
return subscription;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
let subscription: ClientSubscription;
|
|
368
|
+
if (request.sustain) {
|
|
369
|
+
subscription = new SustainedSubscription({
|
|
370
|
+
subscribe,
|
|
371
|
+
peer,
|
|
372
|
+
closed: () => this.#subscriptions.delete(subscription),
|
|
373
|
+
request,
|
|
374
|
+
abort: session?.abort,
|
|
375
|
+
retries: this.#sustainRetries,
|
|
376
|
+
});
|
|
377
|
+
} else {
|
|
378
|
+
subscription = await subscribe(request);
|
|
365
379
|
}
|
|
366
|
-
}
|
|
367
380
|
|
|
368
|
-
|
|
369
|
-
|
|
381
|
+
this.#subscriptions.addActive(subscription);
|
|
382
|
+
|
|
383
|
+
return subscription;
|
|
370
384
|
}
|
|
371
385
|
|
|
372
386
|
async #handleSubscriptionResponse(request: Subscribe, result: ReadResult) {
|
|
@@ -382,18 +396,42 @@ export class ClientInteraction<SessionT extends InteractionSession = Interaction
|
|
|
382
396
|
}
|
|
383
397
|
}
|
|
384
398
|
|
|
385
|
-
#begin(request: Read | Write | Invoke | Subscribe) {
|
|
386
|
-
if (this.#
|
|
399
|
+
async #begin(request: Read | Write | Invoke | Subscribe, session?: SessionT) {
|
|
400
|
+
if (this.#abort.aborted) {
|
|
387
401
|
throw new ImplementationError("Client interaction unavailable after close");
|
|
388
402
|
}
|
|
389
403
|
this.#interactions.add(request);
|
|
390
|
-
}
|
|
391
404
|
|
|
392
|
-
|
|
393
|
-
|
|
405
|
+
const checkAbort = Abort.checkerFor(session);
|
|
406
|
+
|
|
407
|
+
const messenger = await InteractionClientMessenger.create(this.#exchanges);
|
|
408
|
+
|
|
409
|
+
const context: RequestContext = {
|
|
410
|
+
checkAbort,
|
|
411
|
+
messenger,
|
|
412
|
+
[Symbol.asyncDispose]: async () => {
|
|
413
|
+
await messenger.close();
|
|
414
|
+
this.#interactions.delete(request);
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
context.checkAbort();
|
|
420
|
+
} catch (e) {
|
|
421
|
+
await context[Symbol.asyncDispose]();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return context;
|
|
394
425
|
}
|
|
395
426
|
}
|
|
396
427
|
|
|
428
|
+
interface RequestContext {
|
|
429
|
+
checkAbort(): void;
|
|
430
|
+
messenger: InteractionClientMessenger;
|
|
431
|
+
|
|
432
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
433
|
+
}
|
|
434
|
+
|
|
397
435
|
async function* readChunks(messenger: InteractionClientMessenger) {
|
|
398
436
|
for await (const report of messenger.readDataReports()) {
|
|
399
437
|
yield InputChunk(report);
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export * from "./ClientInteraction.js";
|
|
8
|
-
export * from "./ClientSubscription.js";
|
|
9
|
-
export * from "./ClientSubscriptionHandler.js";
|
|
10
|
-
export * from "./ClientSubscriptions.js";
|
|
11
8
|
export * from "./InputChunk.js";
|
|
12
9
|
export * from "./ReadScope.js";
|
|
10
|
+
export * from "./subscription/index.js";
|