zwave-js 8.7.8 → 8.8.2

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.
Files changed (107) hide show
  1. package/build/CommandClass.js +1 -0
  2. package/build/CommandClass.js.map +1 -1
  3. package/build/Controller.d.ts +1 -0
  4. package/build/Controller.d.ts.map +1 -1
  5. package/build/Controller.js +4 -1
  6. package/build/Controller.js.map +1 -1
  7. package/build/Node.d.ts +1 -1
  8. package/build/Node.d.ts.map +1 -1
  9. package/build/Node.js.map +1 -1
  10. package/build/Utils.d.ts +1 -0
  11. package/build/Utils.d.ts.map +1 -1
  12. package/build/Utils.js +7 -1
  13. package/build/Utils.js.map +1 -1
  14. package/build/lib/commandclass/API.d.ts +15 -3
  15. package/build/lib/commandclass/API.d.ts.map +1 -1
  16. package/build/lib/commandclass/API.js +50 -0
  17. package/build/lib/commandclass/API.js.map +1 -1
  18. package/build/lib/commandclass/CommandClass.d.ts +0 -6
  19. package/build/lib/commandclass/CommandClass.d.ts.map +1 -1
  20. package/build/lib/commandclass/CommandClass.js +0 -12
  21. package/build/lib/commandclass/CommandClass.js.map +1 -1
  22. package/build/lib/commandclass/Security2CC.d.ts +1 -10
  23. package/build/lib/commandclass/Security2CC.d.ts.map +1 -1
  24. package/build/lib/commandclass/Security2CC.js +16 -48
  25. package/build/lib/commandclass/Security2CC.js.map +1 -1
  26. package/build/lib/commandclass/SecurityCC.d.ts +2 -9
  27. package/build/lib/commandclass/SecurityCC.d.ts.map +1 -1
  28. package/build/lib/commandclass/SecurityCC.js +9 -53
  29. package/build/lib/commandclass/SecurityCC.js.map +1 -1
  30. package/build/lib/commandclass/WakeUpCC.d.ts.map +1 -1
  31. package/build/lib/commandclass/WakeUpCC.js +1 -0
  32. package/build/lib/commandclass/WakeUpCC.js.map +1 -1
  33. package/build/lib/controller/BridgeApplicationCommandRequest.d.ts +2 -6
  34. package/build/lib/controller/BridgeApplicationCommandRequest.d.ts.map +1 -1
  35. package/build/lib/controller/BridgeApplicationCommandRequest.js +9 -19
  36. package/build/lib/controller/BridgeApplicationCommandRequest.js.map +1 -1
  37. package/build/lib/controller/Controller.d.ts +12 -2
  38. package/build/lib/controller/Controller.d.ts.map +1 -1
  39. package/build/lib/controller/Controller.js +14 -6
  40. package/build/lib/controller/Controller.js.map +1 -1
  41. package/build/lib/controller/Inclusion.d.ts +1 -1
  42. package/build/lib/controller/SendDataBridgeMessages.d.ts +5 -3
  43. package/build/lib/controller/SendDataBridgeMessages.d.ts.map +1 -1
  44. package/build/lib/controller/SendDataBridgeMessages.js +21 -8
  45. package/build/lib/controller/SendDataBridgeMessages.js.map +1 -1
  46. package/build/lib/controller/SendDataMessages.d.ts +5 -3
  47. package/build/lib/controller/SendDataMessages.d.ts.map +1 -1
  48. package/build/lib/controller/SendDataMessages.js +21 -9
  49. package/build/lib/controller/SendDataMessages.js.map +1 -1
  50. package/build/lib/controller/SendDataShared.d.ts +72 -2
  51. package/build/lib/controller/SendDataShared.d.ts.map +1 -1
  52. package/build/lib/controller/SendDataShared.js +195 -1
  53. package/build/lib/controller/SendDataShared.js.map +1 -1
  54. package/build/lib/driver/CommandQueueMachine.d.ts +7 -3
  55. package/build/lib/driver/CommandQueueMachine.d.ts.map +1 -1
  56. package/build/lib/driver/CommandQueueMachine.js +66 -29
  57. package/build/lib/driver/CommandQueueMachine.js.map +1 -1
  58. package/build/lib/driver/Driver.d.ts +16 -12
  59. package/build/lib/driver/Driver.d.ts.map +1 -1
  60. package/build/lib/driver/Driver.js +179 -164
  61. package/build/lib/driver/Driver.js.map +1 -1
  62. package/build/lib/driver/MessageGenerators.d.ts +26 -0
  63. package/build/lib/driver/MessageGenerators.d.ts.map +1 -0
  64. package/build/lib/driver/MessageGenerators.js +246 -0
  65. package/build/lib/driver/MessageGenerators.js.map +1 -0
  66. package/build/lib/driver/SendThreadMachine.d.ts +24 -53
  67. package/build/lib/driver/SendThreadMachine.d.ts.map +1 -1
  68. package/build/lib/driver/SendThreadMachine.js +160 -618
  69. package/build/lib/driver/SendThreadMachine.js.map +1 -1
  70. package/build/lib/driver/StateMachineShared.d.ts +2 -0
  71. package/build/lib/driver/StateMachineShared.d.ts.map +1 -1
  72. package/build/lib/driver/StateMachineShared.js +21 -10
  73. package/build/lib/driver/StateMachineShared.js.map +1 -1
  74. package/build/lib/driver/Transaction.d.ts +35 -3
  75. package/build/lib/driver/Transaction.d.ts.map +1 -1
  76. package/build/lib/driver/Transaction.js +20 -15
  77. package/build/lib/driver/Transaction.js.map +1 -1
  78. package/build/lib/driver/TransactionMachine.d.ts +30 -0
  79. package/build/lib/driver/TransactionMachine.d.ts.map +1 -0
  80. package/build/lib/driver/TransactionMachine.js +247 -0
  81. package/build/lib/driver/TransactionMachine.js.map +1 -0
  82. package/build/lib/driver/ZWaveOptions.d.ts +0 -2
  83. package/build/lib/driver/ZWaveOptions.d.ts.map +1 -1
  84. package/build/lib/message/Constants.d.ts +8 -9
  85. package/build/lib/message/Constants.d.ts.map +1 -1
  86. package/build/lib/message/Constants.js +9 -12
  87. package/build/lib/message/Constants.js.map +1 -1
  88. package/build/lib/message/Message.d.ts +5 -1
  89. package/build/lib/message/Message.d.ts.map +1 -1
  90. package/build/lib/message/Message.js +11 -0
  91. package/build/lib/message/Message.js.map +1 -1
  92. package/build/lib/node/HealthCheck.d.ts +8 -0
  93. package/build/lib/node/HealthCheck.d.ts.map +1 -0
  94. package/build/lib/node/HealthCheck.js +83 -0
  95. package/build/lib/node/HealthCheck.js.map +1 -0
  96. package/build/lib/node/Node.d.ts +11 -3
  97. package/build/lib/node/Node.d.ts.map +1 -1
  98. package/build/lib/node/Node.js +283 -7
  99. package/build/lib/node/Node.js.map +1 -1
  100. package/build/lib/node/Types.d.ts +132 -1
  101. package/build/lib/node/Types.d.ts.map +1 -1
  102. package/build/lib/node/Types.js.map +1 -1
  103. package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.d.ts +14 -0
  104. package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.d.ts.map +1 -0
  105. package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.js +46 -0
  106. package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.js.map +1 -0
  107. package/package.json +14 -14
@@ -5,71 +5,25 @@ const core_1 = require("@zwave-js/core");
5
5
  const sorted_list_1 = require("alcalzone-shared/sorted-list");
6
6
  const xstate_1 = require("xstate");
7
7
  const actions_1 = require("xstate/lib/actions");
8
- const ICommandClassContainer_1 = require("../commandclass/ICommandClassContainer");
9
8
  const NoOperationCC_1 = require("../commandclass/NoOperationCC");
10
- const ApplicationCommandRequest_1 = require("../controller/ApplicationCommandRequest");
11
- const BridgeApplicationCommandRequest_1 = require("../controller/BridgeApplicationCommandRequest");
12
- const SendDataBridgeMessages_1 = require("../controller/SendDataBridgeMessages");
13
- const SendDataMessages_1 = require("../controller/SendDataMessages");
14
- const SendDataShared_1 = require("../controller/SendDataShared");
15
9
  const Constants_1 = require("../message/Constants");
16
10
  const Types_1 = require("../node/Types");
17
11
  const CommandQueueMachine_1 = require("./CommandQueueMachine");
18
- const StateMachineShared_1 = require("./StateMachineShared");
19
- // These actions must be assign actions or they will be executed out of order
20
- const setCurrentTransaction = (0, xstate_1.assign)((ctx) => {
21
- const queue = ctx.queue;
22
- const next = ctx.queue.shift();
23
- return {
24
- ...ctx,
25
- queue,
26
- currentTransaction: next,
27
- };
28
- });
29
- const deleteCurrentTransaction = (0, xstate_1.assign)((ctx) => ({
30
- ...ctx,
31
- currentTransaction: undefined,
32
- }));
33
- const deleteHandshakeTransaction = (0, xstate_1.assign)((ctx) => ({
34
- ...ctx,
35
- handshakeTransaction: undefined,
36
- }));
37
- const resetSendDataAttempts = (0, xstate_1.assign)({
38
- sendDataAttempts: (_) => 0,
39
- });
40
- const incrementSendDataAttempts = (0, xstate_1.assign)({
41
- sendDataAttempts: (ctx) => ctx.sendDataAttempts + 1,
42
- });
12
+ const TransactionMachine_1 = require("./TransactionMachine");
13
+ const finalizeTransaction = (0, actions_1.pure)((ctx, evt) => [
14
+ (0, actions_1.stop)(evt.id),
15
+ (0, xstate_1.assign)((ctx) => {
16
+ var _a;
17
+ // Pause the send thread if necessary
18
+ const transaction = (_a = ctx.activeTransactions.get(evt.id)) === null || _a === void 0 ? void 0 : _a.transaction;
19
+ if (transaction === null || transaction === void 0 ? void 0 : transaction.pauseSendThread)
20
+ ctx.paused = true;
21
+ // Remove the last reference to the actor
22
+ ctx.activeTransactions.delete(evt.id);
23
+ return ctx;
24
+ }),
25
+ ]);
43
26
  const forwardToCommandQueue = (0, xstate_1.forwardTo)((ctx) => ctx.commandQueue);
44
- const currentTransactionIsSendData = (ctx) => { var _a; return (0, SendDataShared_1.isSendData)((_a = ctx.currentTransaction) === null || _a === void 0 ? void 0 : _a.message); };
45
- const forwardNodeUpdate = (0, actions_1.pure)((ctx, evt) => {
46
- return (0, actions_1.raise)({
47
- type: "nodeUpdate",
48
- result: evt.message,
49
- });
50
- });
51
- const forwardHandshakeResponse = (0, actions_1.pure)((ctx, evt) => {
52
- return (0, actions_1.raise)({
53
- type: "handshakeResponse",
54
- result: evt.message,
55
- });
56
- });
57
- const forwardActiveCommandSuccess = (0, actions_1.pure)((ctx, evt) => {
58
- return (0, actions_1.raise)({ ...evt, type: "active_command_success" });
59
- });
60
- const forwardActiveCommandFailure = (0, actions_1.pure)((ctx, evt) => {
61
- return (0, actions_1.raise)({ ...evt, type: "active_command_failure" });
62
- });
63
- const forwardActiveCommandError = (0, actions_1.pure)((ctx, evt) => {
64
- return (0, actions_1.raise)({ ...evt, type: "active_command_error" });
65
- });
66
- const sendCurrentTransactionToCommandQueue = (0, actions_1.send)((ctx) => ({
67
- type: "add",
68
- transaction: ctx.currentTransaction,
69
- }), { to: (ctx) => ctx.commandQueue });
70
- const resetCommandQueue = (0, actions_1.send)("reset", {
71
- to: (ctx) => ctx.commandQueue,
72
- });
73
27
  const sortQueue = (0, xstate_1.assign)({
74
28
  queue: (ctx) => {
75
29
  const queue = ctx.queue;
@@ -80,12 +34,8 @@ const sortQueue = (0, xstate_1.assign)({
80
34
  return queue;
81
35
  },
82
36
  });
83
- const every = (...guards) => ({
84
- type: "every",
85
- guards,
86
- });
87
37
  const guards = {
88
- maySendFirstMessage: (ctx) => {
38
+ mayStartTransaction: (ctx, evt, meta) => {
89
39
  // We may not send anything if the send thread is paused
90
40
  if (ctx.paused)
91
41
  return false;
@@ -102,243 +52,96 @@ const guards = {
102
52
  // If we don't send them, they block the send queue
103
53
  // 3. Nodes that can sleep but do not support wakeup: https://github.com/zwave-js/node-zwave-js/discussions/1537
104
54
  // We need to try and send messages to them even if they are asleep, because we might never hear from them
55
+ // 3.
56
+ if (nextTransaction.priority === Constants_1.MessagePriority.Handshake)
57
+ return true;
58
+ // We may not start any non-handshake transaction while the queue is busy
59
+ if (meta.state.matches("busy"))
60
+ return false;
61
+ // 1./2.
105
62
  return (!targetNode ||
106
63
  targetNode.status !== Types_1.NodeStatus.Asleep ||
107
64
  (!targetNode.supportsCC(core_1.CommandClasses["Wake Up"]) &&
108
65
  targetNode.interviewStage >= Types_1.InterviewStage.NodeInfo) ||
109
- (0, NoOperationCC_1.messageIsPing)(message) ||
110
- nextTransaction.priority === Constants_1.MessagePriority.Handshake);
111
- },
112
- requiresNoHandshake: (ctx) => {
113
- var _a;
114
- const msg = (_a = ctx.currentTransaction) === null || _a === void 0 ? void 0 : _a.message;
115
- if (msg instanceof SendDataMessages_1.SendDataRequest ||
116
- msg instanceof SendDataBridgeMessages_1.SendDataBridgeRequest) {
117
- return !msg.command.requiresPreTransmitHandshake();
118
- }
119
- return true;
120
- },
121
- isForActiveTransaction: (ctx, evt, meta) => {
122
- return (((meta.state.matches("sending.handshake") ||
123
- !!ctx.handshakeTransaction) &&
124
- evt.transaction === ctx.handshakeTransaction) ||
125
- ((meta.state.matches("sending.execute") ||
126
- meta.state.matches("sending.waitForUpdate") ||
127
- !!ctx.currentTransaction) &&
128
- evt.transaction === ctx.currentTransaction));
129
- },
130
- expectsNodeUpdate: (ctx) => {
131
- var _a;
132
- const msg = (_a = ctx.currentTransaction) === null || _a === void 0 ? void 0 : _a.message;
133
- if (msg instanceof SendDataMessages_1.SendDataRequest ||
134
- msg instanceof SendDataBridgeMessages_1.SendDataBridgeRequest) {
135
- return msg.command.expectsCCResponse();
136
- }
137
- return false;
138
- },
139
- isExpectedUpdate: (ctx, evt, meta) => {
140
- if (!meta.state.matches("sending.waitForUpdate"))
141
- return false;
142
- const sentMsg = ctx.currentTransaction.message;
143
- const receivedMsg = evt.message;
144
- return ((receivedMsg instanceof ApplicationCommandRequest_1.ApplicationCommandRequest ||
145
- receivedMsg instanceof BridgeApplicationCommandRequest_1.BridgeApplicationCommandRequest) &&
146
- sentMsg.command.isExpectedCCResponse(receivedMsg.command));
147
- },
148
- currentTransactionIsSendData,
149
- mayRetry: (ctx, evt) => {
150
- const msg = ctx.currentTransaction.message;
151
- if (!(0, SendDataShared_1.isSendData)(msg))
152
- return false;
153
- if (msg instanceof SendDataMessages_1.SendDataMulticastRequest ||
154
- msg instanceof SendDataBridgeMessages_1.SendDataMulticastBridgeRequest) {
155
- // Don't try to resend multicast messages if they were already transmitted.
156
- // One or more nodes might have already reacted
157
- if (evt.reason === "callback NOK") {
158
- return false;
159
- }
160
- }
161
- return msg.maxSendAttempts > ctx.sendDataAttempts;
162
- },
163
- /** Whether the message is an outgoing pre-transmit handshake */
164
- isPreTransmitHandshakeForCurrentTransaction: (ctx, evt, meta) => {
165
- if (!meta.state.matches("sending.handshake"))
166
- return false;
167
- // Ensure that the current transaction is SendData
168
- if (!currentTransactionIsSendData(ctx))
169
- return false;
170
- const transaction = evt.transaction;
171
- if (transaction.priority !== Constants_1.MessagePriority.PreTransmitHandshake)
172
- return false;
173
- if (transaction.message instanceof SendDataMessages_1.SendDataRequest ||
174
- transaction.message instanceof SendDataBridgeMessages_1.SendDataBridgeRequest) {
175
- // require the handshake to be for the same node
176
- return (transaction.message.command.nodeId ===
177
- ctx.currentTransaction.message.command.nodeId);
178
- }
179
- return false;
180
- },
181
- isExpectedHandshakeResponse: (ctx, evt, meta) => {
182
- if (!ctx.handshakeTransaction)
183
- return false;
184
- if (!meta.state.matches("sending.handshake.waitForHandshakeResponse"))
185
- return false;
186
- const sentMsg = ctx.handshakeTransaction.message;
187
- const receivedMsg = evt.message;
188
- if (!(0, ICommandClassContainer_1.isCommandClassContainer)(receivedMsg))
189
- return false;
190
- return ((receivedMsg instanceof ApplicationCommandRequest_1.ApplicationCommandRequest ||
191
- receivedMsg instanceof BridgeApplicationCommandRequest_1.BridgeApplicationCommandRequest) &&
192
- sentMsg.command.isExpectedCCResponse(receivedMsg.command));
193
- },
194
- /** Whether the message is an outgoing handshake response to the current node*/
195
- isHandshakeForCurrentTransaction: (ctx, evt) => {
196
- // First ensure that the current transaction is SendData
197
- if (!currentTransactionIsSendData(ctx))
198
- return false;
199
- // Then ensure that the event transaction is also SendData
200
- const transaction = evt.transaction;
201
- if (transaction.priority !== Constants_1.MessagePriority.Handshake)
202
- return false;
203
- if (transaction.message instanceof SendDataMessages_1.SendDataRequest ||
204
- transaction.message instanceof SendDataBridgeMessages_1.SendDataBridgeRequest) {
205
- // require the handshake to be for the same node
206
- return (transaction.message.command.nodeId ===
207
- ctx.currentTransaction.message.command.nodeId);
208
- }
209
- return false;
210
- },
211
- shouldNotKeepCurrentTransaction: (ctx, evt) => {
212
- const reducer = evt.reducer;
213
- return reducer(ctx.currentTransaction, "current").type !== "keep";
214
- },
215
- currentTransactionIsPingForNode: (ctx, evt) => {
216
- var _a;
217
- const msg = (_a = ctx.currentTransaction) === null || _a === void 0 ? void 0 : _a.message;
218
- return (!!msg &&
219
- (0, NoOperationCC_1.messageIsPing)(msg) &&
220
- msg.getNodeId() === evt.nodeId);
66
+ (0, NoOperationCC_1.messageIsPing)(message));
221
67
  },
68
+ hasNoActiveTransactions: (ctx) => ctx.activeTransactions.size === 0,
222
69
  };
223
- function createMessageDroppedUnexpectedError(original) {
224
- const ret = new core_1.ZWaveError(`Message dropped because of an unexpected error: ${original.message}`, core_1.ZWaveErrorCodes.Controller_MessageDropped);
225
- if (original.stack)
226
- ret.stack = original.stack;
227
- return ret;
228
- }
229
70
  function createSendThreadMachine(implementations, params) {
230
- const resolveCurrentTransaction = (0, xstate_1.assign)((ctx, evt) => {
231
- if (ctx.currentTransaction.pauseSendThread) {
232
- ctx.paused = true;
233
- }
234
- implementations.resolveTransaction(ctx.currentTransaction, evt.result);
235
- return ctx;
236
- });
237
- const resolveCurrentTransactionWithoutMessage = (0, xstate_1.assign)((ctx) => {
238
- if (ctx.currentTransaction.pauseSendThread) {
239
- ctx.paused = true;
240
- }
241
- implementations.resolveTransaction(ctx.currentTransaction, undefined);
242
- return ctx;
243
- });
244
- const rejectCurrentTransaction = (0, xstate_1.assign)((ctx, evt) => {
245
- implementations.rejectTransaction(ctx.currentTransaction, (0, StateMachineShared_1.sendDataErrorToZWaveError)(evt.reason, ctx.currentTransaction, evt.result));
246
- return ctx;
247
- });
248
- const rejectCurrentTransactionWithError = (0, xstate_1.assign)((ctx, evt) => {
249
- implementations.rejectTransaction(ctx.currentTransaction, createMessageDroppedUnexpectedError(evt.error));
250
- return ctx;
251
- });
252
- const rejectCurrentTransactionWithNodeTimeout = (0, xstate_1.assign)((ctx) => {
253
- implementations.rejectTransaction(ctx.currentTransaction, (0, StateMachineShared_1.sendDataErrorToZWaveError)("node timeout", ctx.currentTransaction, undefined));
254
- return ctx;
255
- });
256
- const resolveHandshakeTransaction = (0, xstate_1.assign)((ctx, evt) => {
257
- implementations.resolveTransaction(ctx.handshakeTransaction, evt.result);
258
- return ctx;
259
- });
260
- const rejectHandshakeTransaction = (0, xstate_1.assign)((ctx, evt) => {
261
- implementations.rejectTransaction(ctx.handshakeTransaction, (0, StateMachineShared_1.sendDataErrorToZWaveError)(evt.reason, ctx.handshakeTransaction, evt.result));
262
- return ctx;
263
- });
264
- const rejectHandshakeTransactionWithError = (0, xstate_1.assign)((ctx, evt) => {
265
- implementations.rejectTransaction(ctx.handshakeTransaction, createMessageDroppedUnexpectedError(evt.error));
266
- return ctx;
267
- });
268
- const rejectHandshakeTransactionWithNodeTimeout = (0, xstate_1.assign)((ctx) => {
269
- const hsTransaction = ctx.handshakeTransaction;
270
- if (hsTransaction) {
271
- implementations.rejectTransaction(hsTransaction, (0, StateMachineShared_1.sendDataErrorToZWaveError)("node timeout", hsTransaction, undefined));
272
- }
273
- return ctx;
274
- });
275
- const resolveEventTransaction = (0, xstate_1.assign)((ctx, evt) => {
276
- if (evt.transaction.pauseSendThread) {
277
- ctx.paused = true;
278
- }
279
- implementations.resolveTransaction(evt.transaction, evt.result);
280
- return ctx;
281
- });
282
- const rejectEventTransaction = (0, xstate_1.assign)((ctx, evt) => {
283
- implementations.rejectTransaction(evt.transaction, (0, StateMachineShared_1.sendDataErrorToZWaveError)(evt.reason, evt.transaction.message, evt.result));
284
- return ctx;
285
- });
286
- const rejectEventTransactionWithError = (0, xstate_1.assign)((ctx, evt) => {
287
- implementations.rejectTransaction(evt.transaction, createMessageDroppedUnexpectedError(evt.error));
288
- return ctx;
289
- });
290
71
  const notifyUnsolicited = (_, evt) => {
291
72
  implementations.notifyUnsolicited(evt.message);
292
73
  };
293
- const reduce = (0, xstate_1.assign)({
294
- queue: (ctx, evt) => {
295
- const { queue, currentTransaction } = ctx;
296
- const drop = [];
297
- const requeue = [];
298
- const reduceTransaction = (transaction, source) => {
299
- const reducerResult = evt.reducer(transaction, source);
300
- switch (reducerResult.type) {
301
- case "drop":
302
- drop.push(transaction);
303
- break;
304
- case "requeue":
305
- if (reducerResult.priority != undefined) {
306
- transaction.priority = reducerResult.priority;
307
- }
308
- if (reducerResult.tag != undefined) {
309
- transaction.tag = reducerResult.tag;
310
- }
311
- requeue.push(transaction);
312
- break;
313
- case "resolve":
314
- implementations.resolveTransaction(transaction, reducerResult.message);
315
- drop.push(transaction);
316
- break;
317
- case "reject":
318
- implementations.rejectTransaction(transaction, new core_1.ZWaveError(reducerResult.message, reducerResult.code, undefined, transaction.stack));
319
- drop.push(transaction);
320
- break;
321
- }
322
- };
323
- for (const transaction of queue) {
324
- reduceTransaction(transaction, "queue");
74
+ const reduce = (0, actions_1.pure)((ctx, evt) => {
75
+ const dropQueued = [];
76
+ const stopActive = [];
77
+ const requeue = [];
78
+ const reduceTransaction = (transaction, source) => {
79
+ const reducerResult = evt.reducer(transaction, source);
80
+ switch (reducerResult.type) {
81
+ case "drop":
82
+ (source === "queue" ? dropQueued : stopActive).push(transaction);
83
+ break;
84
+ case "requeue":
85
+ if (reducerResult.priority != undefined) {
86
+ transaction.priority = reducerResult.priority;
87
+ }
88
+ if (reducerResult.tag != undefined) {
89
+ transaction.tag = reducerResult.tag;
90
+ }
91
+ if (source === "active")
92
+ stopActive.push(transaction);
93
+ requeue.push(transaction);
94
+ break;
95
+ case "resolve":
96
+ implementations.resolveTransaction(transaction, reducerResult.message);
97
+ (source === "queue" ? dropQueued : stopActive).push(transaction);
98
+ break;
99
+ case "reject":
100
+ implementations.rejectTransaction(transaction, new core_1.ZWaveError(reducerResult.message, reducerResult.code, undefined, transaction.stack));
101
+ (source === "queue" ? dropQueued : stopActive).push(transaction);
102
+ break;
325
103
  }
326
- if (currentTransaction) {
327
- reduceTransaction(currentTransaction, "current");
328
- }
329
- // Now we know what to do with the transactions
330
- queue.remove(...drop, ...requeue);
331
- queue.add(...requeue);
332
- return queue;
333
- },
104
+ };
105
+ const { queue, activeTransactions } = ctx;
106
+ for (const transaction of queue) {
107
+ reduceTransaction(transaction, "queue");
108
+ }
109
+ for (const { transaction } of activeTransactions.values()) {
110
+ reduceTransaction(transaction, "active");
111
+ }
112
+ // Now we know what to do with the transactions
113
+ queue.remove(...dropQueued, ...requeue);
114
+ queue.add(...requeue);
115
+ return [
116
+ (0, xstate_1.assign)((ctx) => ({
117
+ ...ctx,
118
+ queue,
119
+ })),
120
+ ...stopActive.map((t) => (0, actions_1.send)({ type: "remove", transaction: t }, { to: ctx.commandQueue })),
121
+ ];
122
+ });
123
+ const spawnTransaction = (0, xstate_1.assign)((ctx) => {
124
+ const newCounter = (ctx.counter + 1) % 0xffffffff;
125
+ const id = "T" + newCounter.toString(16).padStart(8, "0");
126
+ const transaction = ctx.queue.shift();
127
+ const machine = (0, xstate_1.spawn)((0, TransactionMachine_1.createTransactionMachine)(id, transaction, implementations), {
128
+ name: id,
129
+ });
130
+ ctx.activeTransactions.set(id, { machine, transaction });
131
+ return {
132
+ ...ctx,
133
+ counter: newCounter,
134
+ };
334
135
  });
335
- const ret = (0, xstate_1.Machine)({
136
+ const ret = (0, xstate_1.createMachine)({
336
137
  id: "SendThread",
337
138
  initial: "init",
139
+ preserveActionOrder: true,
338
140
  context: {
339
141
  commandQueue: undefined,
340
142
  queue: new sorted_list_1.SortedList(),
341
- sendDataAttempts: 0,
143
+ activeTransactions: new Map(),
144
+ counter: 0,
342
145
  paused: false,
343
146
  },
344
147
  on: {
@@ -349,83 +152,43 @@ function createSendThreadMachine(implementations, params) {
349
152
  // messages may come back as "unsolicited", these might be expected updates
350
153
  // we need to run them through the serial API machine to avoid mismatches
351
154
  message: { actions: forwardToCommandQueue },
352
- // resolve/reject any un-interesting transactions if they are done
353
- command_success: [
354
- // If this notification belongs to an active command, forward it
355
- {
356
- cond: "isForActiveTransaction",
357
- actions: forwardActiveCommandSuccess,
358
- },
359
- // otherwise just resolve it
360
- {
361
- actions: resolveEventTransaction,
362
- },
363
- ],
364
- command_failure: [
365
- // If this notification belongs to an active command, forward it
366
- {
367
- cond: "isForActiveTransaction",
368
- actions: forwardActiveCommandFailure,
369
- },
370
- // otherwise just reject it
371
- {
372
- actions: rejectEventTransaction,
373
- },
374
- ],
375
- command_error: [
376
- // If this notification belongs to an active command, forward it
377
- {
378
- cond: "isForActiveTransaction",
379
- actions: forwardActiveCommandError,
380
- },
381
- // otherwise just reject it
382
- {
383
- actions: rejectEventTransactionWithError,
384
- },
385
- ],
386
- // handle newly added messages
387
- add: [
388
- // Trigger outgoing handshakes immediately without queueing
389
- {
390
- cond: "isPreTransmitHandshakeForCurrentTransaction",
391
- actions: [
392
- forwardToCommandQueue,
393
- // and inform the state machine when it is the one we've waited for
394
- (0, xstate_1.assign)({
395
- handshakeTransaction: (_, evt) => evt.transaction,
396
- }),
397
- ],
398
- },
399
- // Forward all handshake messages that could have to do with the current transaction
400
- {
401
- cond: "isHandshakeForCurrentTransaction",
402
- actions: forwardToCommandQueue,
403
- },
404
- {
405
- actions: [
406
- (0, xstate_1.assign)({
407
- queue: (ctx, evt) => {
408
- ctx.queue.add(evt.transaction);
409
- return ctx.queue;
410
- },
411
- }),
155
+ // Forward NIFs to each transaction machine to resolve potential waiting pings
156
+ NIF: {
157
+ actions: (0, actions_1.pure)((ctx, evt) => {
158
+ const activeTransactionMachinesForNode = [
159
+ ...ctx.activeTransactions.values(),
160
+ ]
161
+ .filter(({ transaction }) => transaction.message.getNodeId() ===
162
+ evt.nodeId)
163
+ .map((a) => a.machine.id);
164
+ return [
165
+ ...activeTransactionMachinesForNode.map((id) => (0, actions_1.send)(evt, { to: id })),
166
+ // Sort the send queue and evaluate again whether the next message may be sent
167
+ sortQueue,
412
168
  (0, actions_1.raise)("trigger"),
413
- ],
414
- },
415
- ],
416
- unsolicited: [
417
- // If a message is returned by the serial API, they might be an expected node update
418
- {
419
- cond: "isExpectedHandshakeResponse",
420
- actions: forwardHandshakeResponse,
421
- },
422
- {
423
- cond: "isExpectedUpdate",
424
- actions: forwardNodeUpdate,
425
- },
426
- // Return unsolicited messages to the driver
427
- { actions: notifyUnsolicited },
428
- ],
169
+ ];
170
+ }),
171
+ },
172
+ // handle newly added messages
173
+ add: {
174
+ actions: [
175
+ (0, xstate_1.assign)({
176
+ queue: (ctx, evt) => {
177
+ ctx.queue.add(evt.transaction);
178
+ return ctx.queue;
179
+ },
180
+ }),
181
+ (0, actions_1.raise)("trigger"),
182
+ ],
183
+ },
184
+ reduce: {
185
+ // Reducing may reorder the queue, so raise a trigger afterwards
186
+ actions: [reduce, (0, actions_1.raise)("trigger")],
187
+ },
188
+ // Return unsolicited messages to the driver
189
+ unsolicited: {
190
+ actions: notifyUnsolicited,
191
+ },
429
192
  // Accept external commands to sort the queue
430
193
  sortQueue: {
431
194
  actions: [sortQueue, (0, actions_1.raise)("trigger")],
@@ -440,281 +203,63 @@ function createSendThreadMachine(implementations, params) {
440
203
  (0, actions_1.raise)("trigger"),
441
204
  ],
442
205
  },
206
+ // forward events between child machinies
207
+ forward: {
208
+ actions: (0, actions_1.send)((_, evt) => ({ ...evt.payload, from: evt.from }), {
209
+ to: (_, evt) => evt.to,
210
+ }),
211
+ },
212
+ // Stop transactions when they are done
213
+ transaction_done: {
214
+ actions: [finalizeTransaction, (0, actions_1.raise)("trigger")],
215
+ },
443
216
  },
444
217
  states: {
445
218
  init: {
446
219
  entry: (0, xstate_1.assign)({
447
220
  commandQueue: () => (0, xstate_1.spawn)((0, CommandQueueMachine_1.createCommandQueueMachine)(implementations, params), {
448
- name: "commandQueue",
221
+ name: "QUEUE",
449
222
  }),
450
223
  }),
451
224
  // Spawn the command queue when starting the send thread
452
225
  always: "idle",
453
226
  },
227
+ // While idle, any transaction may be started
454
228
  idle: {
455
229
  id: "idle",
456
- entry: [deleteCurrentTransaction, resetSendDataAttempts],
457
- always: [
458
- { cond: "maySendFirstMessage", target: "sending" },
459
- ],
460
- on: {
461
- trigger: [
462
- {
463
- cond: "maySendFirstMessage",
464
- target: "sending",
465
- },
466
- ],
467
- reduce: {
468
- // Reducing may reorder the queue, so raise a trigger afterwards
469
- actions: [reduce, (0, actions_1.raise)("trigger")],
470
- },
230
+ always: {
231
+ cond: "mayStartTransaction",
232
+ // Use the first transaction in the queue as the current one
233
+ actions: spawnTransaction,
234
+ target: "busy",
471
235
  },
472
- },
473
- sending: {
474
- id: "sending",
475
- // Use the first transaction in the queue as the current one
476
- entry: setCurrentTransaction,
477
- initial: "beforeSend",
478
236
  on: {
479
- NIF: {
480
- // Pings are not retransmitted and won't receive a response if the node wake up after the ping was sent
481
- // Therefore resolve pending pings so the communication may proceed immediately
482
- cond: "currentTransactionIsPingForNode",
483
- actions: [
484
- resolveCurrentTransactionWithoutMessage,
485
- // TODO:
486
- // this.driver.controllerLog.logNode(
487
- // node.id,
488
- // `Treating the node info as a successful ping...`,
489
- // );
490
- ],
491
- target: "sending.done",
492
- internal: true,
493
- },
494
- reduce: [
495
- // If the current transaction should not be kept, tell the send queue to abort it and go back to idle
496
- {
497
- cond: "shouldNotKeepCurrentTransaction",
498
- actions: [resetCommandQueue, reduce],
499
- target: "sending.done",
500
- internal: true,
501
- },
502
- { actions: reduce },
503
- ],
237
+ // On trigger, re-evaluate the conditions to enter "busy"
238
+ trigger: { target: "idle" },
504
239
  },
505
- states: {
506
- beforeSend: {
507
- entry: [
508
- (0, actions_1.pure)((ctx) => currentTransactionIsSendData(ctx)
509
- ? incrementSendDataAttempts
510
- : undefined),
511
- deleteHandshakeTransaction,
512
- ],
513
- always: [
514
- // Skip this step if no handshake is required
515
- {
516
- cond: "requiresNoHandshake",
517
- target: "execute",
518
- },
519
- // else begin the handshake process
520
- {
521
- target: "handshake",
522
- },
523
- ],
524
- },
525
- handshake: {
526
- // Just send the handshake as a side effect
527
- invoke: {
528
- id: "preTransmitHandshake",
529
- src: "preTransmitHandshake",
530
- onDone: "#sending.execute",
531
- },
532
- initial: "waitForCommandResult",
533
- on: {
534
- handshakeResponse: {
535
- actions: resolveHandshakeTransaction,
536
- },
537
- },
538
- states: {
539
- // After kicking off the command, wait until it is completed
540
- waitForCommandResult: {
541
- on: {
542
- // On success, start waiting for the handshake response
543
- active_command_success: "waitForHandshakeResponse",
544
- active_command_failure: [
545
- // On failure, retry SendData commands if possible
546
- {
547
- cond: "mayRetry",
548
- actions: rejectHandshakeTransaction,
549
- target: "#sending.retryWait",
550
- },
551
- // Otherwise reject the transaction
552
- {
553
- actions: [
554
- rejectHandshakeTransaction,
555
- rejectCurrentTransaction,
556
- ],
557
- target: "#sending.done",
558
- },
559
- ],
560
- active_command_error: [
561
- // On failure, retry SendData commands if possible
562
- {
563
- cond: "mayRetry",
564
- actions: rejectHandshakeTransactionWithError,
565
- target: "#sending.retryWait",
566
- },
567
- // Otherwise reject the transaction
568
- {
569
- actions: [
570
- rejectHandshakeTransactionWithError,
571
- rejectCurrentTransactionWithError,
572
- ],
573
- target: "#sending.done",
574
- },
575
- ],
576
- },
577
- },
578
- waitForHandshakeResponse: {
579
- after: {
580
- // If an update times out, retry if possible - otherwise reject the entire transaction
581
- REPORT_TIMEOUT: [
582
- // only retry on timeout when configured
583
- ...(params.attempts
584
- .retryAfterTransmitReport
585
- ? [
586
- {
587
- cond: "mayRetry",
588
- target: "#sending.retryWait",
589
- actions: rejectHandshakeTransactionWithNodeTimeout,
590
- },
591
- ]
592
- : []),
593
- {
594
- actions: [
595
- rejectHandshakeTransactionWithNodeTimeout,
596
- rejectCurrentTransactionWithNodeTimeout,
597
- ],
598
- target: "#sending.done",
599
- },
600
- ],
601
- },
602
- },
603
- },
604
- },
605
- execute: {
606
- entry: [
607
- deleteHandshakeTransaction,
608
- sendCurrentTransactionToCommandQueue,
609
- ],
610
- on: {
611
- active_command_success: [
612
- // On success, start waiting for an update
613
- {
614
- cond: "expectsNodeUpdate",
615
- target: "waitForUpdate",
616
- },
617
- // or resolve the current transaction if none is required
618
- {
619
- actions: resolveCurrentTransaction,
620
- target: "done",
621
- },
622
- ],
623
- active_command_failure: [
624
- // On failure, retry SendData commands if possible
625
- {
626
- cond: every("currentTransactionIsSendData", "mayRetry"),
627
- target: "retryWait",
628
- },
629
- // Otherwise reject the transaction
630
- {
631
- actions: rejectCurrentTransaction,
632
- target: "done",
633
- },
634
- ],
635
- active_command_error: [
636
- // On failure, retry SendData commands if possible
637
- {
638
- cond: every("currentTransactionIsSendData", "mayRetry"),
639
- target: "retryWait",
640
- },
641
- // Otherwise reject the transaction
642
- {
643
- actions: rejectCurrentTransactionWithError,
644
- target: "done",
645
- },
646
- ],
647
- },
648
- },
649
- waitForUpdate: {
650
- on: {
651
- nodeUpdate: {
652
- actions: resolveCurrentTransaction,
653
- target: "done",
654
- },
655
- resend: {
656
- // We were asked to resend the pending transaction immediately
657
- // without increasing the retry counter
658
- target: "execute",
659
- },
660
- },
661
- after: {
662
- // If an update times out, retry if possible - otherwise reject the transaction
663
- REPORT_TIMEOUT: [
664
- // only retry on timeout when configured
665
- ...(params.attempts.retryAfterTransmitReport
666
- ? [
667
- {
668
- cond: "mayRetry",
669
- target: "retryWait",
670
- },
671
- ]
672
- : []),
673
- {
674
- actions: rejectCurrentTransactionWithNodeTimeout,
675
- target: "done",
676
- },
677
- ],
678
- },
679
- },
680
- retryWait: {
681
- invoke: {
682
- id: "notify",
683
- src: "notifyRetry",
684
- },
685
- after: {
686
- 500: "beforeSend",
687
- },
240
+ },
241
+ // While busy, only handshake responses may be sent
242
+ busy: {
243
+ id: "busy",
244
+ always: [
245
+ {
246
+ cond: "hasNoActiveTransactions",
247
+ target: "idle",
688
248
  },
689
- done: {
690
- // Clean up the context after sending
691
- always: {
692
- target: "#idle",
693
- actions: [
694
- deleteCurrentTransaction,
695
- deleteHandshakeTransaction,
696
- resetSendDataAttempts,
697
- ],
698
- },
249
+ {
250
+ cond: "mayStartTransaction",
251
+ // Use the first transaction in the queue as the current one
252
+ actions: spawnTransaction,
253
+ target: "busy",
699
254
  },
255
+ ],
256
+ on: {
257
+ // On trigger, re-evaluate the conditions to go spawn transactions or back to idle
258
+ trigger: { target: "busy" },
700
259
  },
701
260
  },
702
261
  },
703
262
  }, {
704
- services: {
705
- preTransmitHandshake: async (ctx) => {
706
- // Execute the pre transmit handshake and swallow all errors
707
- try {
708
- await ctx.currentTransaction.message.command.preTransmitHandshake();
709
- }
710
- catch (e) { }
711
- },
712
- notifyRetry: (ctx) => {
713
- var _a;
714
- (_a = implementations.notifyRetry) === null || _a === void 0 ? void 0 : _a.call(implementations, "SendData", undefined, ctx.currentTransaction.message, ctx.sendDataAttempts, ctx.currentTransaction.message.maxSendAttempts, 500);
715
- return Promise.resolve();
716
- },
717
- },
718
263
  guards: {
719
264
  ...guards,
720
265
  every: (ctx, event, { cond }) => {
@@ -722,9 +267,6 @@ function createSendThreadMachine(implementations, params) {
722
267
  return keys.every((guardKey) => guards[guardKey](ctx, event, undefined));
723
268
  },
724
269
  },
725
- delays: {
726
- REPORT_TIMEOUT: params.timeouts.report,
727
- },
728
270
  });
729
271
  return ret;
730
272
  }