zigbee-herdsman 6.0.1 → 6.0.3

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 (260) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/adapter/ezsp/driver/uart.js +1 -1
  3. package/dist/adapter/ezsp/driver/uart.js.map +1 -1
  4. package/dist/adapter/z-stack/adapter/zStackAdapter.js +4 -4
  5. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
  6. package/dist/adapter/zigate/adapter/zigateAdapter.js +4 -4
  7. package/dist/adapter/zigate/adapter/zigateAdapter.js.map +1 -1
  8. package/dist/controller/model/device.d.ts.map +1 -1
  9. package/dist/controller/model/device.js +1 -0
  10. package/dist/controller/model/device.js.map +1 -1
  11. package/package.json +14 -6
  12. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  13. package/.github/dependabot.yml +0 -22
  14. package/.github/workflows/ci.yml +0 -64
  15. package/.github/workflows/release-please.yml +0 -18
  16. package/.github/workflows/stale.yml +0 -20
  17. package/.github/workflows/typedoc.yaml +0 -47
  18. package/.release-please-manifest.json +0 -3
  19. package/.vscode/extensions.json +0 -3
  20. package/.vscode/settings.json +0 -11
  21. package/biome.json +0 -98
  22. package/examples/join-and-log.js +0 -24
  23. package/release-please-config.json +0 -9
  24. package/src/adapter/adapter.ts +0 -189
  25. package/src/adapter/adapterDiscovery.ts +0 -666
  26. package/src/adapter/const.ts +0 -12
  27. package/src/adapter/deconz/adapter/deconzAdapter.ts +0 -877
  28. package/src/adapter/deconz/driver/constants.ts +0 -246
  29. package/src/adapter/deconz/driver/driver.ts +0 -1540
  30. package/src/adapter/deconz/driver/frame.ts +0 -11
  31. package/src/adapter/deconz/driver/frameParser.ts +0 -753
  32. package/src/adapter/deconz/driver/parser.ts +0 -45
  33. package/src/adapter/deconz/driver/writer.ts +0 -22
  34. package/src/adapter/deconz/types.d.ts +0 -13
  35. package/src/adapter/ember/adapter/emberAdapter.ts +0 -2265
  36. package/src/adapter/ember/adapter/endpoints.ts +0 -86
  37. package/src/adapter/ember/adapter/oneWaitress.ts +0 -324
  38. package/src/adapter/ember/adapter/tokensManager.ts +0 -782
  39. package/src/adapter/ember/consts.ts +0 -178
  40. package/src/adapter/ember/enums.ts +0 -1746
  41. package/src/adapter/ember/ezsp/buffalo.ts +0 -1392
  42. package/src/adapter/ember/ezsp/consts.ts +0 -148
  43. package/src/adapter/ember/ezsp/enums.ts +0 -1114
  44. package/src/adapter/ember/ezsp/ezsp.ts +0 -9061
  45. package/src/adapter/ember/ezspError.ts +0 -10
  46. package/src/adapter/ember/types.ts +0 -866
  47. package/src/adapter/ember/uart/ash.ts +0 -1960
  48. package/src/adapter/ember/uart/consts.ts +0 -109
  49. package/src/adapter/ember/uart/enums.ts +0 -192
  50. package/src/adapter/ember/uart/parser.ts +0 -48
  51. package/src/adapter/ember/uart/queues.ts +0 -247
  52. package/src/adapter/ember/uart/writer.ts +0 -53
  53. package/src/adapter/ember/utils/initters.ts +0 -58
  54. package/src/adapter/ember/utils/math.ts +0 -73
  55. package/src/adapter/events.ts +0 -21
  56. package/src/adapter/ezsp/adapter/backup.ts +0 -109
  57. package/src/adapter/ezsp/adapter/ezspAdapter.ts +0 -614
  58. package/src/adapter/ezsp/driver/commands.ts +0 -2497
  59. package/src/adapter/ezsp/driver/consts.ts +0 -11
  60. package/src/adapter/ezsp/driver/driver.ts +0 -1002
  61. package/src/adapter/ezsp/driver/ezsp.ts +0 -802
  62. package/src/adapter/ezsp/driver/frame.ts +0 -101
  63. package/src/adapter/ezsp/driver/index.ts +0 -4
  64. package/src/adapter/ezsp/driver/multicast.ts +0 -78
  65. package/src/adapter/ezsp/driver/parser.ts +0 -81
  66. package/src/adapter/ezsp/driver/types/basic.ts +0 -201
  67. package/src/adapter/ezsp/driver/types/index.ts +0 -239
  68. package/src/adapter/ezsp/driver/types/named.ts +0 -2330
  69. package/src/adapter/ezsp/driver/types/struct.ts +0 -844
  70. package/src/adapter/ezsp/driver/uart.ts +0 -460
  71. package/src/adapter/ezsp/driver/utils/crc16ccitt.ts +0 -44
  72. package/src/adapter/ezsp/driver/utils/index.ts +0 -32
  73. package/src/adapter/ezsp/driver/writer.ts +0 -64
  74. package/src/adapter/index.ts +0 -3
  75. package/src/adapter/serialPort.ts +0 -58
  76. package/src/adapter/socketPortUtils.ts +0 -16
  77. package/src/adapter/tstype.ts +0 -78
  78. package/src/adapter/z-stack/adapter/adapter-backup.ts +0 -519
  79. package/src/adapter/z-stack/adapter/adapter-nv-memory.ts +0 -457
  80. package/src/adapter/z-stack/adapter/endpoints.ts +0 -57
  81. package/src/adapter/z-stack/adapter/manager.ts +0 -543
  82. package/src/adapter/z-stack/adapter/tstype.ts +0 -6
  83. package/src/adapter/z-stack/adapter/zStackAdapter.ts +0 -1190
  84. package/src/adapter/z-stack/constants/af.ts +0 -27
  85. package/src/adapter/z-stack/constants/common.ts +0 -285
  86. package/src/adapter/z-stack/constants/dbg.ts +0 -23
  87. package/src/adapter/z-stack/constants/index.ts +0 -11
  88. package/src/adapter/z-stack/constants/mac.ts +0 -128
  89. package/src/adapter/z-stack/constants/sapi.ts +0 -25
  90. package/src/adapter/z-stack/constants/sys.ts +0 -72
  91. package/src/adapter/z-stack/constants/util.ts +0 -82
  92. package/src/adapter/z-stack/constants/utils.ts +0 -14
  93. package/src/adapter/z-stack/constants/zdo.ts +0 -103
  94. package/src/adapter/z-stack/models/startup-options.ts +0 -13
  95. package/src/adapter/z-stack/structs/entries/address-manager-entry.ts +0 -44
  96. package/src/adapter/z-stack/structs/entries/address-manager-table.ts +0 -19
  97. package/src/adapter/z-stack/structs/entries/aps-link-key-data-entry.ts +0 -12
  98. package/src/adapter/z-stack/structs/entries/aps-link-key-data-table.ts +0 -21
  99. package/src/adapter/z-stack/structs/entries/aps-tc-link-key-entry.ts +0 -19
  100. package/src/adapter/z-stack/structs/entries/aps-tc-link-key-table.ts +0 -21
  101. package/src/adapter/z-stack/structs/entries/channel-list.ts +0 -8
  102. package/src/adapter/z-stack/structs/entries/has-configured.ts +0 -16
  103. package/src/adapter/z-stack/structs/entries/index.ts +0 -16
  104. package/src/adapter/z-stack/structs/entries/nib.ts +0 -66
  105. package/src/adapter/z-stack/structs/entries/nwk-key-descriptor.ts +0 -15
  106. package/src/adapter/z-stack/structs/entries/nwk-key.ts +0 -13
  107. package/src/adapter/z-stack/structs/entries/nwk-pan-id.ts +0 -8
  108. package/src/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.ts +0 -20
  109. package/src/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.ts +0 -19
  110. package/src/adapter/z-stack/structs/entries/security-manager-entry.ts +0 -33
  111. package/src/adapter/z-stack/structs/entries/security-manager-table.ts +0 -22
  112. package/src/adapter/z-stack/structs/index.ts +0 -4
  113. package/src/adapter/z-stack/structs/serializable-memory-object.ts +0 -14
  114. package/src/adapter/z-stack/structs/struct.ts +0 -367
  115. package/src/adapter/z-stack/structs/table.ts +0 -198
  116. package/src/adapter/z-stack/unpi/constants.ts +0 -33
  117. package/src/adapter/z-stack/unpi/frame.ts +0 -62
  118. package/src/adapter/z-stack/unpi/index.ts +0 -4
  119. package/src/adapter/z-stack/unpi/parser.ts +0 -56
  120. package/src/adapter/z-stack/unpi/writer.ts +0 -21
  121. package/src/adapter/z-stack/utils/channel-list.ts +0 -40
  122. package/src/adapter/z-stack/utils/index.ts +0 -2
  123. package/src/adapter/z-stack/utils/network-options.ts +0 -26
  124. package/src/adapter/z-stack/znp/buffaloZnp.ts +0 -175
  125. package/src/adapter/z-stack/znp/definition.ts +0 -2713
  126. package/src/adapter/z-stack/znp/index.ts +0 -2
  127. package/src/adapter/z-stack/znp/parameterType.ts +0 -22
  128. package/src/adapter/z-stack/znp/tstype.ts +0 -44
  129. package/src/adapter/z-stack/znp/utils.ts +0 -10
  130. package/src/adapter/z-stack/znp/znp.ts +0 -342
  131. package/src/adapter/z-stack/znp/zpiObject.ts +0 -148
  132. package/src/adapter/zboss/adapter/zbossAdapter.ts +0 -526
  133. package/src/adapter/zboss/commands.ts +0 -1184
  134. package/src/adapter/zboss/consts.ts +0 -9
  135. package/src/adapter/zboss/driver.ts +0 -422
  136. package/src/adapter/zboss/enums.ts +0 -360
  137. package/src/adapter/zboss/frame.ts +0 -227
  138. package/src/adapter/zboss/reader.ts +0 -65
  139. package/src/adapter/zboss/types.ts +0 -0
  140. package/src/adapter/zboss/uart.ts +0 -428
  141. package/src/adapter/zboss/utils.ts +0 -58
  142. package/src/adapter/zboss/writer.ts +0 -49
  143. package/src/adapter/zigate/adapter/patchZdoBuffaloBE.ts +0 -27
  144. package/src/adapter/zigate/adapter/zigateAdapter.ts +0 -618
  145. package/src/adapter/zigate/driver/LICENSE +0 -17
  146. package/src/adapter/zigate/driver/buffaloZiGate.ts +0 -212
  147. package/src/adapter/zigate/driver/commandType.ts +0 -418
  148. package/src/adapter/zigate/driver/constants.ts +0 -150
  149. package/src/adapter/zigate/driver/frame.ts +0 -197
  150. package/src/adapter/zigate/driver/messageType.ts +0 -287
  151. package/src/adapter/zigate/driver/parameterType.ts +0 -32
  152. package/src/adapter/zigate/driver/ziGateObject.ts +0 -146
  153. package/src/adapter/zigate/driver/zigate.ts +0 -423
  154. package/src/adapter/zoh/adapter/utils.ts +0 -27
  155. package/src/adapter/zoh/adapter/zohAdapter.ts +0 -838
  156. package/src/buffalo/buffalo.ts +0 -342
  157. package/src/buffalo/index.ts +0 -1
  158. package/src/controller/controller.ts +0 -1022
  159. package/src/controller/database.ts +0 -124
  160. package/src/controller/events.ts +0 -52
  161. package/src/controller/greenPower.ts +0 -603
  162. package/src/controller/helpers/index.ts +0 -1
  163. package/src/controller/helpers/installCodes.ts +0 -107
  164. package/src/controller/helpers/request.ts +0 -96
  165. package/src/controller/helpers/requestQueue.ts +0 -125
  166. package/src/controller/helpers/zclFrameConverter.ts +0 -47
  167. package/src/controller/helpers/zclTransactionSequenceNumber.ts +0 -19
  168. package/src/controller/index.ts +0 -6
  169. package/src/controller/model/device.ts +0 -1248
  170. package/src/controller/model/endpoint.ts +0 -1105
  171. package/src/controller/model/entity.ts +0 -23
  172. package/src/controller/model/group.ts +0 -424
  173. package/src/controller/model/index.ts +0 -5
  174. package/src/controller/model/zigbeeEntity.ts +0 -30
  175. package/src/controller/touchlink.ts +0 -189
  176. package/src/controller/tstype.ts +0 -274
  177. package/src/index.ts +0 -12
  178. package/src/models/backup-storage-legacy.ts +0 -48
  179. package/src/models/backup-storage-unified.ts +0 -47
  180. package/src/models/backup.ts +0 -37
  181. package/src/models/index.ts +0 -5
  182. package/src/models/network-options.ts +0 -11
  183. package/src/utils/backup.ts +0 -152
  184. package/src/utils/index.ts +0 -5
  185. package/src/utils/logger.ts +0 -20
  186. package/src/utils/patchBigIntSerialization.ts +0 -8
  187. package/src/utils/queue.ts +0 -76
  188. package/src/utils/types.d.ts +0 -3
  189. package/src/utils/utils.ts +0 -19
  190. package/src/utils/wait.ts +0 -5
  191. package/src/utils/waitress.ts +0 -96
  192. package/src/zspec/consts.ts +0 -84
  193. package/src/zspec/enums.ts +0 -22
  194. package/src/zspec/index.ts +0 -3
  195. package/src/zspec/tstypes.ts +0 -18
  196. package/src/zspec/utils.ts +0 -247
  197. package/src/zspec/zcl/buffaloZcl.ts +0 -1220
  198. package/src/zspec/zcl/definition/cluster.ts +0 -5915
  199. package/src/zspec/zcl/definition/clusters-typegen.ts +0 -588
  200. package/src/zspec/zcl/definition/clusters-types.ts +0 -7331
  201. package/src/zspec/zcl/definition/consts.ts +0 -24
  202. package/src/zspec/zcl/definition/enums.ts +0 -203
  203. package/src/zspec/zcl/definition/foundation.ts +0 -329
  204. package/src/zspec/zcl/definition/manufacturerCode.ts +0 -729
  205. package/src/zspec/zcl/definition/status.ts +0 -69
  206. package/src/zspec/zcl/definition/tstype.ts +0 -377
  207. package/src/zspec/zcl/index.ts +0 -11
  208. package/src/zspec/zcl/utils.ts +0 -321
  209. package/src/zspec/zcl/zclFrame.ts +0 -356
  210. package/src/zspec/zcl/zclHeader.ts +0 -102
  211. package/src/zspec/zcl/zclStatusError.ts +0 -10
  212. package/src/zspec/zdo/buffaloZdo.ts +0 -2336
  213. package/src/zspec/zdo/definition/clusters.ts +0 -722
  214. package/src/zspec/zdo/definition/consts.ts +0 -16
  215. package/src/zspec/zdo/definition/enums.ts +0 -99
  216. package/src/zspec/zdo/definition/status.ts +0 -105
  217. package/src/zspec/zdo/definition/tstypes.ts +0 -1062
  218. package/src/zspec/zdo/index.ts +0 -7
  219. package/src/zspec/zdo/utils.ts +0 -76
  220. package/src/zspec/zdo/zdoStatusError.ts +0 -10
  221. package/test/adapter/adapter.test.ts +0 -1062
  222. package/test/adapter/ember/ash.test.ts +0 -337
  223. package/test/adapter/ember/consts.ts +0 -131
  224. package/test/adapter/ember/emberAdapter.test.ts +0 -3449
  225. package/test/adapter/ember/ezsp.test.ts +0 -385
  226. package/test/adapter/ember/ezspBuffalo.test.ts +0 -93
  227. package/test/adapter/ember/ezspError.test.ts +0 -12
  228. package/test/adapter/ember/math.test.ts +0 -206
  229. package/test/adapter/ezsp/frame.test.ts +0 -30
  230. package/test/adapter/ezsp/uart.test.ts +0 -181
  231. package/test/adapter/z-stack/adapter.test.ts +0 -3984
  232. package/test/adapter/z-stack/constants.test.ts +0 -33
  233. package/test/adapter/z-stack/structs.test.ts +0 -115
  234. package/test/adapter/z-stack/unpi.test.ts +0 -213
  235. package/test/adapter/z-stack/znp.test.ts +0 -1284
  236. package/test/adapter/zboss/fixZdoResponse.test.ts +0 -179
  237. package/test/adapter/zigate/patchZdoBuffaloBE.test.ts +0 -81
  238. package/test/adapter/zigate/zdo.test.ts +0 -187
  239. package/test/adapter/zoh/utils.test.ts +0 -36
  240. package/test/adapter/zoh/zohAdapter.test.ts +0 -1307
  241. package/test/buffalo.test.ts +0 -431
  242. package/test/controller.bench.ts +0 -193
  243. package/test/controller.test.ts +0 -8702
  244. package/test/greenpower.test.ts +0 -1408
  245. package/test/mockAdapters.ts +0 -65
  246. package/test/mockDevices.ts +0 -598
  247. package/test/requests.bench.ts +0 -206
  248. package/test/testUtils.ts +0 -20
  249. package/test/tsconfig.json +0 -9
  250. package/test/utils/math.ts +0 -19
  251. package/test/utils.test.ts +0 -279
  252. package/test/vitest.config.mts +0 -27
  253. package/test/zcl.test.ts +0 -2831
  254. package/test/zspec/utils.test.ts +0 -68
  255. package/test/zspec/zcl/buffalo.test.ts +0 -1374
  256. package/test/zspec/zcl/frame.test.ts +0 -960
  257. package/test/zspec/zcl/utils.test.ts +0 -273
  258. package/test/zspec/zdo/buffalo.test.ts +0 -1850
  259. package/test/zspec/zdo/utils.test.ts +0 -241
  260. package/tsconfig.json +0 -24
@@ -1,3449 +0,0 @@
1
- import {existsSync, mkdirSync, unlinkSync, writeFileSync} from "node:fs";
2
- import path from "node:path";
3
- import {EventEmitter} from "node:stream";
4
- import {afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
5
- import type {TsType} from "../../../src/adapter";
6
- import {
7
- DEFAULT_APS_OPTIONS,
8
- DEFAULT_STACK_CONFIG,
9
- EmberAdapter,
10
- type LinkKeyBackupData,
11
- type NetworkCache,
12
- } from "../../../src/adapter/ember/adapter/emberAdapter";
13
- import {FIXED_ENDPOINTS} from "../../../src/adapter/ember/adapter/endpoints";
14
- import {OneWaitressEvents} from "../../../src/adapter/ember/adapter/oneWaitress";
15
- import {EMBER_LOW_RAM_CONCENTRATOR, SECURITY_LEVEL_Z3} from "../../../src/adapter/ember/consts";
16
- import {
17
- EmberApsOption,
18
- EmberDeviceUpdate,
19
- EmberIncomingMessageType,
20
- EmberJoinDecision,
21
- EmberJoinMethod,
22
- EmberKeyStructBitmask,
23
- EmberNetworkStatus,
24
- EmberNodeType,
25
- EmberOutgoingMessageType,
26
- EmberVersionType,
27
- EzspStatus,
28
- IEEE802154CcaMode,
29
- SecManDerivedKeyType,
30
- SecManFlag,
31
- SecManKeyType,
32
- SLStatus,
33
- } from "../../../src/adapter/ember/enums";
34
- import {EZSP_MIN_PROTOCOL_VERSION, EZSP_PROTOCOL_VERSION, EZSP_STACK_TYPE_MESH} from "../../../src/adapter/ember/ezsp/consts";
35
- import {EzspConfigId, EzspDecisionBitmask, EzspEndpointFlag, EzspPolicyId, EzspValueId} from "../../../src/adapter/ember/ezsp/enums";
36
- import type {EmberEzspEventMap} from "../../../src/adapter/ember/ezsp/ezsp";
37
- import {EzspError} from "../../../src/adapter/ember/ezspError";
38
- import type {
39
- EmberApsFrame,
40
- EmberMulticastTableEntry,
41
- EmberNetworkInitStruct,
42
- EmberNetworkParameters,
43
- EmberVersion,
44
- SecManAPSKeyMetadata,
45
- SecManContext,
46
- SecManKey,
47
- SecManNetworkKeyInfo,
48
- } from "../../../src/adapter/ember/types";
49
- import {lowHighBytes} from "../../../src/adapter/ember/utils/math";
50
- import type {DeviceJoinedPayload, DeviceLeavePayload, ZclPayload} from "../../../src/adapter/events";
51
- import type {AdapterOptions, NetworkOptions, SerialPortOptions} from "../../../src/adapter/tstype";
52
- import type {Backup} from "../../../src/models/backup";
53
- import type {UnifiedBackupStorage} from "../../../src/models/backup-storage-unified";
54
- import {logger} from "../../../src/utils/logger";
55
- import * as ZSpec from "../../../src/zspec";
56
- import type {Eui64, NodeId, PanId} from "../../../src/zspec/tstypes";
57
- import * as Zcl from "../../../src/zspec/zcl";
58
- import * as Zdo from "../../../src/zspec/zdo";
59
- import type * as ZdoTypes from "../../../src/zspec/zdo/definition/tstypes";
60
-
61
- // https://github.com/jestjs/jest/issues/6028#issuecomment-567669082
62
- function defuseRejection<T>(promise: Promise<T>) {
63
- promise.catch(() => {});
64
-
65
- return promise;
66
- }
67
-
68
- function deepClone<T>(obj: T): T {
69
- return JSON.parse(JSON.stringify(obj));
70
- }
71
-
72
- function reverseApsFrame(apsFrame: EmberApsFrame): EmberApsFrame {
73
- return Object.assign({}, apsFrame, {sourceEndpoint: apsFrame.destinationEndpoint, destinationEndpoint: apsFrame.sourceEndpoint});
74
- }
75
-
76
- async function flushPromises(): Promise<void> {
77
- const {setImmediate} = await vi.importActual<typeof import("node:timers")>("node:timers");
78
- return new Promise(setImmediate);
79
- }
80
-
81
- const TEMP_PATH = path.resolve("temp");
82
- const STACK_CONFIG_PATH = path.join(TEMP_PATH, "stack_config.json");
83
- const DEFAULT_NETWORK_OPTIONS: Readonly<NetworkOptions> = {
84
- panID: 24404,
85
- extendedPanID: [118, 185, 136, 236, 199, 244, 246, 85],
86
- channelList: [20],
87
- networkKey: [72, 97, 39, 230, 92, 72, 101, 148, 64, 225, 250, 214, 195, 31, 105, 71],
88
- networkKeyDistribute: false,
89
- };
90
- const DEFAULT_SERIAL_PORT_OPTIONS: Readonly<SerialPortOptions> = {
91
- baudRate: 115200,
92
- rtscts: false,
93
- path: "MOCK",
94
- adapter: "ember",
95
- };
96
- const DEFAULT_ADAPTER_OPTIONS: Readonly<AdapterOptions> = {
97
- concurrent: 16,
98
- disableLED: false,
99
- };
100
- const DEFAULT_BACKUP: Readonly<UnifiedBackupStorage> = {
101
- metadata: {
102
- format: "zigpy/open-coordinator-backup",
103
- version: 1,
104
- source: "zigbee-herdsman@0.55.0",
105
- internal: {
106
- date: "2024-07-19T15:57:15.163Z",
107
- ezspVersion: 13,
108
- },
109
- },
110
- stack_specific: {
111
- ezsp: {
112
- hashed_tclk: "da85e5bac80c8a958b14d44f14c2ba16",
113
- },
114
- },
115
- coordinator_ieee: "1122334455667788",
116
- pan_id: "5f54",
117
- extended_pan_id: "76b988ecc7f4f655",
118
- nwk_update_id: 0,
119
- security_level: 5,
120
- channel: 20,
121
- channel_mask: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],
122
- network_key: {
123
- key: "486127e65c48659440e1fad6c31f6947",
124
- sequence_number: 0,
125
- frame_counter: 16434,
126
- },
127
- devices: [],
128
- };
129
- const DEFAULT_COORDINATOR_IEEE: Eui64 = ZSpec.Utils.eui64LEBufferToHex(Buffer.from(DEFAULT_BACKUP.coordinator_ieee, "hex"));
130
- const DEFAULT_ADAPTER_NETWORK_PARAMETERS: EmberNetworkParameters = {
131
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
132
- panId: DEFAULT_NETWORK_OPTIONS.panID,
133
- radioTxPower: 5,
134
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
135
- joinMethod: 0,
136
- nwkManagerId: 0,
137
- nwkUpdateId: 0,
138
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
139
- };
140
-
141
- let mockManufCode = Zcl.ManufacturerCode.SILICON_LABORATORIES;
142
- let mockAPSSequence = -1; // start at 0
143
- let mockMessageTag = -1; // start at 0
144
- let mockEzspEmitter = new EventEmitter<EmberEzspEventMap>();
145
- const mockEzspRemoveAllListeners = vi.fn().mockImplementation((e) => {
146
- mockEzspEmitter.removeAllListeners(e);
147
- });
148
- const mockEzspOn = vi.fn().mockImplementation((e, l) => {
149
- mockEzspEmitter.on(e, l);
150
- });
151
- const mockEzspOnce = vi.fn().mockImplementation((e, l) => {
152
- mockEzspEmitter.once(e, l);
153
- });
154
- const mockEzspStart = vi.fn().mockResolvedValue(EzspStatus.SUCCESS);
155
- const mockEzspStop = vi.fn();
156
-
157
- const mockEzspSend = vi.fn().mockResolvedValue([SLStatus.OK, ++mockMessageTag]);
158
- const mockEzspSetMulticastTableEntry = vi.fn().mockResolvedValue(SLStatus.OK);
159
- const mockEzspSetManufacturerCode = vi.fn().mockImplementation((code) => {
160
- mockManufCode = code;
161
- });
162
- const mockEzspReadAndClearCounters = vi.fn().mockResolvedValue([1, 2, 3, 4]); // not matching EmberCounterType, but doesn't matter here
163
- const mockEzspGetNetworkParameters = vi
164
- .fn()
165
- .mockResolvedValue([SLStatus.OK, EmberNodeType.COORDINATOR, deepClone(DEFAULT_ADAPTER_NETWORK_PARAMETERS)]);
166
- const mockEzspNetworkState = vi.fn().mockResolvedValue(EmberNetworkStatus.JOINED_NETWORK);
167
- const mockEzspGetEui64 = vi.fn().mockResolvedValue(DEFAULT_COORDINATOR_IEEE);
168
- const mockEzspSetConcentrator = vi.fn().mockResolvedValue(SLStatus.OK);
169
- const mockEzspSetSourceRouteDiscoveryMode = vi.fn().mockResolvedValue(1240 /* ms */);
170
- const mockEzspSetRadioIeee802154CcaMode = vi.fn().mockResolvedValue(SLStatus.OK);
171
- // not OK by default since used to detected unreged EP
172
- const mockEzspGetEndpointFlags = vi.fn().mockResolvedValue([SLStatus.NOT_FOUND, EzspEndpointFlag.DISABLED]);
173
- const mockEzspAddEndpoint = vi.fn().mockResolvedValue(SLStatus.OK);
174
- const mockEzspNetworkInit = vi.fn().mockImplementation((_networkInitStruct: EmberNetworkInitStruct) => {
175
- setTimeout(async () => {
176
- mockEzspEmitter.emit("stackStatus", SLStatus.NETWORK_UP);
177
- await flushPromises();
178
- }, 300);
179
-
180
- return SLStatus.OK;
181
- });
182
- const mockEzspExportKey = vi.fn().mockImplementation((context: SecManContext) => {
183
- switch (context.coreKeyType) {
184
- case SecManKeyType.NETWORK: {
185
- return [SLStatus.OK, {contents: Buffer.from(DEFAULT_BACKUP.network_key.key, "hex")} as SecManKey];
186
- }
187
- case SecManKeyType.TC_LINK: {
188
- return [SLStatus.OK, {contents: Buffer.from(DEFAULT_BACKUP.stack_specific!.ezsp!.hashed_tclk!, "hex")} as SecManKey];
189
- }
190
- }
191
- });
192
- const mockEzspLeaveNetwork = vi.fn().mockImplementation(() => {
193
- setTimeout(async () => {
194
- mockEzspEmitter.emit("stackStatus", SLStatus.NETWORK_DOWN);
195
- await flushPromises();
196
- }, 300);
197
-
198
- return SLStatus.OK;
199
- });
200
- const mockEzspSetInitialSecurityState = vi.fn().mockResolvedValue(SLStatus.OK);
201
- const mockEzspSetExtendedSecurityBitmask = vi.fn().mockResolvedValue(SLStatus.OK);
202
- const mockEzspClearKeyTable = vi.fn().mockResolvedValue(SLStatus.OK);
203
- const mockEzspFormNetwork = vi.fn().mockImplementation((_parameters: EmberNetworkParameters) => {
204
- setTimeout(async () => {
205
- mockEzspEmitter.emit("stackStatus", SLStatus.NETWORK_UP);
206
- await flushPromises();
207
- }, 300);
208
-
209
- return SLStatus.OK;
210
- });
211
- const mockEzspStartWritingStackTokens = vi.fn().mockResolvedValue(SLStatus.OK);
212
- const mockEzspGetConfigurationValue = vi.fn().mockImplementation((config: EzspConfigId) => {
213
- switch (config) {
214
- case EzspConfigId.KEY_TABLE_SIZE: {
215
- return [SLStatus.OK, 0];
216
- }
217
- }
218
- });
219
- const mockEzspExportLinkKeyByIndex = vi.fn();
220
- const mockEzspEraseKeyTableEntry = vi.fn().mockResolvedValue(SLStatus.OK);
221
- const mockEzspImportLinkKey = vi.fn().mockResolvedValue(SLStatus.OK);
222
- const mockEzspBroadcastNextNetworkKey = vi.fn().mockResolvedValue(SLStatus.OK);
223
- const mockEzspBroadcastNetworkKeySwitch = vi.fn().mockResolvedValue(SLStatus.OK);
224
- const mockEzspStartScan = vi.fn().mockResolvedValue(SLStatus.OK);
225
- const mockEzspVersion = vi.fn().mockImplementation((version: number) => [version, EZSP_STACK_TYPE_MESH, 0]);
226
- const mockEzspSetProtocolVersion = vi.fn();
227
- const mockEzspGetVersionStruct = vi.fn().mockResolvedValue([
228
- SLStatus.OK,
229
- {
230
- build: 135,
231
- major: 8,
232
- minor: 0,
233
- patch: 0,
234
- special: 0,
235
- type: EmberVersionType.GA,
236
- } as EmberVersion,
237
- ]);
238
- const mockEzspSetConfigurationValue = vi.fn().mockResolvedValue(SLStatus.OK);
239
- const mockEzspSetValue = vi.fn().mockResolvedValue(SLStatus.OK);
240
- const mockEzspSetPolicy = vi.fn().mockResolvedValue(SLStatus.OK);
241
- const mockEzspPermitJoining = vi.fn().mockImplementation((duration: number) => {
242
- setTimeout(async () => {
243
- mockEzspEmitter.emit("stackStatus", duration > 0 ? SLStatus.ZIGBEE_NETWORK_OPENED : SLStatus.ZIGBEE_NETWORK_CLOSED);
244
- await flushPromises();
245
- }, 300);
246
-
247
- return SLStatus.OK;
248
- });
249
- const mockEzspSendBroadcast = vi.fn().mockResolvedValue([SLStatus.OK, ++mockAPSSequence]);
250
- const mockEzspSendUnicast = vi.fn().mockResolvedValue([SLStatus.OK, ++mockAPSSequence]);
251
- const mockEzspGetNetworkKeyInfo = vi.fn().mockResolvedValue([
252
- SLStatus.OK,
253
- {
254
- networkKeySet: true,
255
- alternateNetworkKeySet: false,
256
- networkKeySequenceNumber: DEFAULT_BACKUP.network_key.sequence_number,
257
- altNetworkKeySequenceNumber: 0,
258
- networkKeyFrameCounter: DEFAULT_BACKUP.network_key.frame_counter,
259
- } as SecManNetworkKeyInfo,
260
- ]);
261
- const mockEzspGetApsKeyInfo = vi.fn().mockResolvedValue([
262
- SLStatus.OK,
263
- {
264
- bitmask: EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
265
- outgoingFrameCounter: 456,
266
- incomingFrameCounter: 0,
267
- ttlInSeconds: 0,
268
- } as SecManAPSKeyMetadata,
269
- ]);
270
- const mockEzspSetRadioPower = vi.fn().mockResolvedValue(SLStatus.OK);
271
- const mockEzspImportTransientKey = vi.fn().mockResolvedValue(SLStatus.OK);
272
- const mockEzspClearTransientLinkKeys = vi.fn().mockResolvedValue(SLStatus.OK);
273
- const mockEzspSetLogicalAndRadioChannel = vi.fn().mockResolvedValue(SLStatus.OK);
274
- const mockEzspSendRawMessage = vi.fn().mockResolvedValue(SLStatus.OK);
275
- const mockEzspSetNWKFrameCounter = vi.fn().mockResolvedValue(SLStatus.OK);
276
- const mockEzspSetAPSFrameCounter = vi.fn().mockResolvedValue(SLStatus.OK);
277
-
278
- vi.mock("../../../src/adapter/ember/uart/ash");
279
-
280
- vi.mock("../../../src/adapter/ember/ezsp/ezsp", async (importOriginal) => ({
281
- ...(await importOriginal()),
282
- Ezsp: vi.fn(() => ({
283
- removeAllListeners: mockEzspRemoveAllListeners,
284
- on: mockEzspOn,
285
- once: mockEzspOnce,
286
-
287
- // only functions called from adapter
288
- ash: {readAndClearCounters: vi.fn().mockReturnValue([9, 8, 7])},
289
-
290
- start: mockEzspStart,
291
- stop: mockEzspStop,
292
- send: mockEzspSend,
293
- ezspSetMulticastTableEntry: mockEzspSetMulticastTableEntry,
294
- ezspSetManufacturerCode: mockEzspSetManufacturerCode,
295
- ezspReadAndClearCounters: mockEzspReadAndClearCounters,
296
- ezspGetNetworkParameters: mockEzspGetNetworkParameters,
297
- ezspNetworkState: mockEzspNetworkState,
298
- ezspGetEui64: mockEzspGetEui64,
299
- ezspSetConcentrator: mockEzspSetConcentrator,
300
- ezspSetSourceRouteDiscoveryMode: mockEzspSetSourceRouteDiscoveryMode,
301
- ezspSetRadioIeee802154CcaMode: mockEzspSetRadioIeee802154CcaMode,
302
- ezspGetEndpointFlags: mockEzspGetEndpointFlags,
303
- ezspAddEndpoint: mockEzspAddEndpoint,
304
- ezspNetworkInit: mockEzspNetworkInit,
305
- ezspExportKey: mockEzspExportKey,
306
- ezspLeaveNetwork: mockEzspLeaveNetwork,
307
- ezspSetInitialSecurityState: mockEzspSetInitialSecurityState,
308
- ezspSetExtendedSecurityBitmask: mockEzspSetExtendedSecurityBitmask,
309
- ezspClearKeyTable: mockEzspClearKeyTable,
310
- ezspFormNetwork: mockEzspFormNetwork,
311
- ezspStartWritingStackTokens: mockEzspStartWritingStackTokens,
312
- ezspGetConfigurationValue: mockEzspGetConfigurationValue,
313
- ezspExportLinkKeyByIndex: mockEzspExportLinkKeyByIndex,
314
- ezspEraseKeyTableEntry: mockEzspEraseKeyTableEntry,
315
- ezspImportLinkKey: mockEzspImportLinkKey,
316
- ezspBroadcastNextNetworkKey: mockEzspBroadcastNextNetworkKey,
317
- ezspBroadcastNetworkKeySwitch: mockEzspBroadcastNetworkKeySwitch,
318
- ezspStartScan: mockEzspStartScan,
319
- ezspVersion: mockEzspVersion,
320
- setProtocolVersion: mockEzspSetProtocolVersion,
321
- ezspGetVersionStruct: mockEzspGetVersionStruct,
322
- ezspSetConfigurationValue: mockEzspSetConfigurationValue,
323
- ezspSetValue: mockEzspSetValue,
324
- ezspSetPolicy: mockEzspSetPolicy,
325
- ezspPermitJoining: mockEzspPermitJoining,
326
- ezspSendBroadcast: mockEzspSendBroadcast,
327
- ezspSendUnicast: mockEzspSendUnicast,
328
- ezspGetNetworkKeyInfo: mockEzspGetNetworkKeyInfo,
329
- ezspGetApsKeyInfo: mockEzspGetApsKeyInfo,
330
- ezspSetRadioPower: mockEzspSetRadioPower,
331
- ezspImportTransientKey: mockEzspImportTransientKey,
332
- ezspClearTransientLinkKeys: mockEzspClearTransientLinkKeys,
333
- ezspSetLogicalAndRadioChannel: mockEzspSetLogicalAndRadioChannel,
334
- ezspSendRawMessage: mockEzspSendRawMessage,
335
- ezspSetNWKFrameCounter: mockEzspSetNWKFrameCounter,
336
- ezspSetAPSFrameCounter: mockEzspSetAPSFrameCounter,
337
- })),
338
- }));
339
-
340
- const ezspMocks = [
341
- mockEzspRemoveAllListeners,
342
- mockEzspOn,
343
- mockEzspOnce,
344
- mockEzspStart,
345
- mockEzspStop,
346
- mockEzspSend,
347
- mockEzspSetMulticastTableEntry,
348
- mockEzspSetManufacturerCode,
349
- mockEzspReadAndClearCounters,
350
- mockEzspGetNetworkParameters,
351
- mockEzspNetworkState,
352
- mockEzspGetEui64,
353
- mockEzspSetConcentrator,
354
- mockEzspSetSourceRouteDiscoveryMode,
355
- mockEzspSetRadioIeee802154CcaMode,
356
- mockEzspGetEndpointFlags,
357
- mockEzspAddEndpoint,
358
- mockEzspNetworkInit,
359
- mockEzspExportKey,
360
- mockEzspLeaveNetwork,
361
- mockEzspSetInitialSecurityState,
362
- mockEzspSetExtendedSecurityBitmask,
363
- mockEzspClearKeyTable,
364
- mockEzspFormNetwork,
365
- mockEzspStartWritingStackTokens,
366
- mockEzspGetConfigurationValue,
367
- mockEzspExportLinkKeyByIndex,
368
- mockEzspEraseKeyTableEntry,
369
- mockEzspImportLinkKey,
370
- mockEzspBroadcastNextNetworkKey,
371
- mockEzspBroadcastNetworkKeySwitch,
372
- mockEzspStartScan,
373
- mockEzspVersion,
374
- mockEzspSetProtocolVersion,
375
- mockEzspGetVersionStruct,
376
- mockEzspSetConfigurationValue,
377
- mockEzspSetValue,
378
- mockEzspSetPolicy,
379
- mockEzspPermitJoining,
380
- mockEzspSendBroadcast,
381
- mockEzspSendUnicast,
382
- mockEzspGetNetworkKeyInfo,
383
- mockEzspGetApsKeyInfo,
384
- mockEzspSetRadioPower,
385
- mockEzspImportTransientKey,
386
- mockEzspClearTransientLinkKeys,
387
- mockEzspSetLogicalAndRadioChannel,
388
- mockEzspSendRawMessage,
389
- mockEzspSetNWKFrameCounter,
390
- mockEzspSetAPSFrameCounter,
391
- ];
392
-
393
- describe("Ember Adapter Layer", () => {
394
- let adapter: EmberAdapter;
395
- let backupPath: string;
396
- const loggerSpies = {
397
- debug: vi.spyOn(logger, "debug"),
398
- info: vi.spyOn(logger, "info"),
399
- warning: vi.spyOn(logger, "warning"),
400
- error: vi.spyOn(logger, "error"),
401
- };
402
-
403
- const deleteCoordinatorBackup = () => {
404
- if (existsSync(backupPath)) {
405
- unlinkSync(backupPath);
406
- }
407
- };
408
-
409
- const deleteStackConfig = () => {
410
- if (existsSync(STACK_CONFIG_PATH)) {
411
- unlinkSync(STACK_CONFIG_PATH);
412
- }
413
- };
414
-
415
- const takeResetCodePath = () => {
416
- deleteCoordinatorBackup();
417
- mockEzspGetNetworkParameters.mockResolvedValueOnce([
418
- SLStatus.OK,
419
- EmberNodeType.COORDINATOR,
420
- {
421
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
422
- panId: 1234,
423
- radioTxPower: 5,
424
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
425
- joinMethod: 0,
426
- nwkManagerId: 0,
427
- nwkUpdateId: 0,
428
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
429
- } as EmberNetworkParameters,
430
- ]);
431
- };
432
-
433
- const takeRestoredCodePath = () => {
434
- mockEzspGetNetworkParameters.mockResolvedValueOnce([
435
- SLStatus.OK,
436
- EmberNodeType.COORDINATOR,
437
- {
438
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
439
- panId: 1234,
440
- radioTxPower: 5,
441
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
442
- joinMethod: 0,
443
- nwkManagerId: 0,
444
- nwkUpdateId: 0,
445
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
446
- } as EmberNetworkParameters,
447
- ]);
448
- };
449
-
450
- const clearMocks = () => {
451
- for (const mock of ezspMocks) {
452
- mock.mockClear();
453
- }
454
-
455
- loggerSpies.debug.mockClear();
456
- loggerSpies.info.mockClear();
457
- loggerSpies.warning.mockClear();
458
- loggerSpies.error.mockClear();
459
- };
460
-
461
- beforeAll(() => {
462
- if (!existsSync(TEMP_PATH)) {
463
- mkdirSync(TEMP_PATH);
464
- } else {
465
- // just in case, remove previous remnants
466
- deleteCoordinatorBackup();
467
- deleteStackConfig();
468
- }
469
- });
470
-
471
- afterAll(() => {
472
- deleteCoordinatorBackup();
473
- deleteStackConfig();
474
- });
475
-
476
- beforeEach(() => {
477
- vi.useFakeTimers();
478
-
479
- backupPath = path.join(TEMP_PATH, "ember_coordinator_backup.json");
480
-
481
- writeFileSync(backupPath, JSON.stringify(DEFAULT_BACKUP, undefined, 2));
482
-
483
- mockManufCode = Zcl.ManufacturerCode.SILICON_LABORATORIES;
484
- mockAPSSequence = -1;
485
- mockMessageTag = -1;
486
- // make sure emitter is reset too
487
- mockEzspEmitter = new EventEmitter();
488
-
489
- clearMocks();
490
- });
491
-
492
- afterEach(() => {
493
- vi.useRealTimers();
494
- });
495
-
496
- it("Creates default instance", () => {
497
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
498
-
499
- expect(adapter).toBeInstanceOf(EmberAdapter);
500
- expect(adapter.stackConfig).toStrictEqual(DEFAULT_STACK_CONFIG);
501
- });
502
-
503
- it("Loads custom stack config", () => {
504
- const config = {
505
- CONCENTRATOR_RAM_TYPE: "low",
506
- CONCENTRATOR_MIN_TIME: 1,
507
- CONCENTRATOR_MAX_TIME: 31,
508
- CONCENTRATOR_ROUTE_ERROR_THRESHOLD: 5,
509
- CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD: 2,
510
- CONCENTRATOR_MAX_HOPS: 5,
511
- MAX_END_DEVICE_CHILDREN: 16,
512
- TRANSIENT_DEVICE_TIMEOUT: 1000,
513
- END_DEVICE_POLL_TIMEOUT: 12,
514
- TRANSIENT_KEY_TIMEOUT_S: 500,
515
- CCA_MODE: "SIGNAL_AND_RSSI",
516
- };
517
-
518
- writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
519
-
520
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
521
-
522
- expect(adapter.stackConfig).toStrictEqual(config);
523
-
524
- // cleanup
525
- unlinkSync(STACK_CONFIG_PATH);
526
- });
527
-
528
- it("Loads only valid custom stack config", () => {
529
- const config = {
530
- CONCENTRATOR_RAM_TYPE: "bad",
531
- CONCENTRATOR_MIN_TIME: -1,
532
- CONCENTRATOR_MAX_TIME: 15,
533
- CONCENTRATOR_ROUTE_ERROR_THRESHOLD: 500,
534
- CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD: 200,
535
- CONCENTRATOR_MAX_HOPS: 35,
536
- MAX_END_DEVICE_CHILDREN: 65,
537
- TRANSIENT_DEVICE_TIMEOUT: 65536,
538
- END_DEVICE_POLL_TIMEOUT: 15,
539
- TRANSIENT_KEY_TIMEOUT_S: 65536,
540
- CCA_MODE: "abcd",
541
- };
542
-
543
- writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
544
-
545
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
546
-
547
- expect(adapter.stackConfig).toStrictEqual(DEFAULT_STACK_CONFIG);
548
-
549
- // cleanup
550
- unlinkSync(STACK_CONFIG_PATH);
551
- });
552
-
553
- it("Loads only valid custom stack config - null CCA_MODE", () => {
554
- const config = {
555
- CCA_MODE: null,
556
- };
557
-
558
- writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
559
-
560
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
561
-
562
- expect(adapter.stackConfig).toStrictEqual(DEFAULT_STACK_CONFIG);
563
-
564
- // cleanup
565
- unlinkSync(STACK_CONFIG_PATH);
566
- });
567
-
568
- it("Uses default concurrency for queue if not supplied/valid", () => {
569
- adapter = new EmberAdapter(
570
- DEFAULT_NETWORK_OPTIONS,
571
- DEFAULT_SERIAL_PORT_OPTIONS,
572
- backupPath,
573
- Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {concurrent: undefined}),
574
- );
575
- // @ts-expect-error private
576
- expect(adapter.queue.concurrent).toStrictEqual(16);
577
- });
578
-
579
- it("Starts with resumed when everything matches", async () => {
580
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
581
- const result = adapter.start();
582
-
583
- await vi.advanceTimersByTimeAsync(5000);
584
- await expect(result).resolves.toStrictEqual("resumed");
585
- expect(mockEzspSetProtocolVersion).toHaveBeenCalledWith(EZSP_PROTOCOL_VERSION);
586
- expect(
587
- // @ts-expect-error private
588
- adapter.networkCache,
589
- ).toStrictEqual({
590
- eui64: DEFAULT_COORDINATOR_IEEE,
591
- parameters: {
592
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
593
- panId: DEFAULT_NETWORK_OPTIONS.panID,
594
- radioTxPower: 5,
595
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
596
- joinMethod: 0,
597
- nwkManagerId: 0,
598
- nwkUpdateId: 0,
599
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
600
- } as EmberNetworkParameters,
601
- } as NetworkCache);
602
- });
603
-
604
- it("Starts with custom stack config", async () => {
605
- const config = {
606
- CONCENTRATOR_RAM_TYPE: "low",
607
- CONCENTRATOR_MIN_TIME: 1,
608
- CONCENTRATOR_MAX_TIME: 31,
609
- CONCENTRATOR_ROUTE_ERROR_THRESHOLD: 5,
610
- CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD: 2,
611
- CONCENTRATOR_MAX_HOPS: 5,
612
- MAX_END_DEVICE_CHILDREN: 16,
613
- TRANSIENT_DEVICE_TIMEOUT: 1000,
614
- END_DEVICE_POLL_TIMEOUT: 12,
615
- TRANSIENT_KEY_TIMEOUT_S: 500,
616
- CCA_MODE: "SIGNAL_AND_RSSI",
617
- };
618
-
619
- writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
620
-
621
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
622
- const result = adapter.start();
623
-
624
- await vi.advanceTimersByTimeAsync(5000);
625
- await expect(result).resolves.toStrictEqual("resumed");
626
- expect(mockEzspSetValue).toHaveBeenCalledWith(EzspValueId.TRANSIENT_DEVICE_TIMEOUT, 2, lowHighBytes(config.TRANSIENT_DEVICE_TIMEOUT));
627
- expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.MAX_END_DEVICE_CHILDREN, config.MAX_END_DEVICE_CHILDREN);
628
- expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.END_DEVICE_POLL_TIMEOUT, config.END_DEVICE_POLL_TIMEOUT);
629
- expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, config.TRANSIENT_KEY_TIMEOUT_S);
630
- expect(mockEzspSetConcentrator).toHaveBeenCalledWith(
631
- true,
632
- EMBER_LOW_RAM_CONCENTRATOR,
633
- config.CONCENTRATOR_MIN_TIME,
634
- config.CONCENTRATOR_MAX_TIME,
635
- config.CONCENTRATOR_ROUTE_ERROR_THRESHOLD,
636
- config.CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD,
637
- config.CONCENTRATOR_MAX_HOPS,
638
- );
639
- expect(mockEzspSetRadioIeee802154CcaMode).toHaveBeenCalledWith(IEEE802154CcaMode.SIGNAL_AND_RSSI);
640
-
641
- // cleanup
642
- unlinkSync(STACK_CONFIG_PATH);
643
- });
644
-
645
- it("Starts with custom stack config invalid CCA_MODE", async () => {
646
- const config = {
647
- CCA_MODE: "abcd",
648
- };
649
-
650
- writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
651
-
652
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
653
- const result = adapter.start();
654
-
655
- await vi.advanceTimersByTimeAsync(5000);
656
- await expect(result).resolves.toStrictEqual("resumed");
657
- expect(mockEzspSetRadioIeee802154CcaMode).toHaveBeenCalledTimes(0);
658
-
659
- // cleanup
660
- unlinkSync(STACK_CONFIG_PATH);
661
- });
662
-
663
- it("Starts with restored when no network in adapter", async () => {
664
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
665
- const expectedNetParams: EmberNetworkParameters = {
666
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
667
- panId: DEFAULT_NETWORK_OPTIONS.panID,
668
- radioTxPower: 5,
669
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
670
- joinMethod: 0,
671
- nwkManagerId: 0,
672
- nwkUpdateId: 0,
673
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
674
- };
675
-
676
- mockEzspNetworkInit.mockResolvedValueOnce(SLStatus.NOT_JOINED);
677
-
678
- const result = adapter.start();
679
-
680
- await vi.advanceTimersByTimeAsync(5000);
681
- expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.network_key.frame_counter);
682
- // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.???.???);
683
- expect(mockEzspFormNetwork).toHaveBeenCalledWith(expectedNetParams);
684
- await expect(result).resolves.toStrictEqual("restored");
685
- });
686
-
687
- it("Starts with restored when network param mismatch but backup available", async () => {
688
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
689
- const expectedNetParams: EmberNetworkParameters = {
690
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
691
- panId: DEFAULT_NETWORK_OPTIONS.panID,
692
- radioTxPower: 5,
693
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
694
- joinMethod: 0,
695
- nwkManagerId: 0,
696
- nwkUpdateId: 0,
697
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
698
- };
699
-
700
- mockEzspGetNetworkParameters.mockResolvedValueOnce([
701
- SLStatus.OK,
702
- EmberNodeType.COORDINATOR,
703
- {
704
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
705
- panId: 1234,
706
- radioTxPower: 5,
707
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
708
- joinMethod: 0,
709
- nwkManagerId: 0,
710
- nwkUpdateId: 0,
711
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
712
- } as EmberNetworkParameters,
713
- ]);
714
-
715
- const result = adapter.start();
716
-
717
- await vi.advanceTimersByTimeAsync(5000);
718
- expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.network_key.frame_counter);
719
- // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.???.???);
720
- expect(mockEzspFormNetwork).toHaveBeenCalledWith(expectedNetParams);
721
- await expect(result).resolves.toStrictEqual("restored");
722
- });
723
-
724
- it("Starts with restored when network key mismatch but backup available", async () => {
725
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
726
- const expectedNetParams: EmberNetworkParameters = {
727
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
728
- panId: DEFAULT_NETWORK_OPTIONS.panID,
729
- radioTxPower: 5,
730
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
731
- joinMethod: 0,
732
- nwkManagerId: 0,
733
- nwkUpdateId: 0,
734
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
735
- };
736
-
737
- mockEzspGetNetworkParameters.mockResolvedValueOnce([SLStatus.OK, EmberNodeType.COORDINATOR, expectedNetParams]);
738
-
739
- const contents = Buffer.from(DEFAULT_BACKUP.network_key.key, "hex").fill(0xff);
740
-
741
- mockEzspExportKey.mockResolvedValueOnce([SLStatus.OK, {contents} as SecManKey]);
742
-
743
- const result = adapter.start();
744
-
745
- await vi.advanceTimersByTimeAsync(5000);
746
- await expect(result).resolves.toStrictEqual("restored");
747
- expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.network_key.frame_counter);
748
- // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledWith(DEFAULT_BACKUP.???.???);
749
- expect(mockEzspFormNetwork).toHaveBeenCalledWith(expectedNetParams);
750
- });
751
-
752
- it("Starts with reset when networks mismatch but no backup available", async () => {
753
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
754
-
755
- deleteCoordinatorBackup();
756
- mockEzspGetNetworkParameters.mockResolvedValueOnce([
757
- SLStatus.OK,
758
- EmberNodeType.COORDINATOR,
759
- {
760
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
761
- panId: 1234,
762
- radioTxPower: 5,
763
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
764
- joinMethod: 0,
765
- nwkManagerId: 0,
766
- nwkUpdateId: 0,
767
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
768
- } as EmberNetworkParameters,
769
- ]);
770
-
771
- const result = adapter.start();
772
-
773
- await vi.advanceTimersByTimeAsync(5000);
774
- await expect(result).resolves.toStrictEqual("reset");
775
- });
776
-
777
- it("Starts with reset when backup/config mismatch", async () => {
778
- adapter = new EmberAdapter(
779
- Object.assign({}, DEFAULT_NETWORK_OPTIONS, {panID: 1234}),
780
- DEFAULT_SERIAL_PORT_OPTIONS,
781
- backupPath,
782
- DEFAULT_ADAPTER_OPTIONS,
783
- );
784
-
785
- const result = adapter.start();
786
-
787
- await vi.advanceTimersByTimeAsync(5000);
788
- await expect(result).resolves.toStrictEqual("reset");
789
- expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledTimes(0);
790
- // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledTimes(0);
791
- expect(mockEzspFormNetwork).toHaveBeenCalledWith({
792
- panId: 1234,
793
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
794
- radioTxPower: 5, // default when setting `transmitPower` is null/zero
795
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
796
- joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
797
- nwkManagerId: ZSpec.COORDINATOR_ADDRESS,
798
- nwkUpdateId: 0,
799
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
800
- } as EmberNetworkParameters);
801
- });
802
-
803
- it("Starts with reset and forms with given transmit power", async () => {
804
- adapter = new EmberAdapter(
805
- Object.assign({}, DEFAULT_NETWORK_OPTIONS, {panID: 1234}),
806
- DEFAULT_SERIAL_PORT_OPTIONS,
807
- backupPath,
808
- Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
809
- );
810
-
811
- const result = adapter.start();
812
-
813
- await vi.advanceTimersByTimeAsync(5000);
814
- await expect(result).resolves.toStrictEqual("reset");
815
- expect(mockEzspSetNWKFrameCounter).toHaveBeenCalledTimes(0);
816
- // expect(mockEzspSetAPSFrameCounter).toHaveBeenCalledTimes(0);
817
- expect(mockEzspFormNetwork).toHaveBeenCalledWith({
818
- panId: 1234,
819
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
820
- radioTxPower: 10,
821
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
822
- joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
823
- nwkManagerId: ZSpec.COORDINATOR_ADDRESS,
824
- nwkUpdateId: 0,
825
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
826
- } as EmberNetworkParameters);
827
- });
828
-
829
- it("Starts with mismatching transmit power", async () => {
830
- adapter = new EmberAdapter(
831
- DEFAULT_NETWORK_OPTIONS,
832
- DEFAULT_SERIAL_PORT_OPTIONS,
833
- backupPath,
834
- Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
835
- );
836
-
837
- const result = adapter.start();
838
-
839
- await vi.advanceTimersByTimeAsync(5000);
840
- await expect(result).resolves.toStrictEqual("resumed");
841
- expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1);
842
- expect(mockEzspSetRadioPower).toHaveBeenCalledWith(10);
843
- });
844
-
845
- it("Starts with matching transmit power after form", async () => {
846
- adapter = new EmberAdapter(
847
- DEFAULT_NETWORK_OPTIONS,
848
- DEFAULT_SERIAL_PORT_OPTIONS,
849
- backupPath,
850
- Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
851
- );
852
- mockEzspNetworkInit.mockResolvedValueOnce(SLStatus.NOT_JOINED);
853
- mockEzspGetNetworkParameters.mockResolvedValueOnce([
854
- SLStatus.OK,
855
- EmberNodeType.COORDINATOR,
856
- {
857
- extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
858
- panId: DEFAULT_NETWORK_OPTIONS.panID,
859
- radioTxPower: 10,
860
- radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
861
- joinMethod: 0,
862
- nwkManagerId: 0,
863
- nwkUpdateId: 0,
864
- channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
865
- } as EmberNetworkParameters,
866
- ]);
867
-
868
- const result = adapter.start();
869
-
870
- await vi.advanceTimersByTimeAsync(5000);
871
- await expect(result).resolves.toStrictEqual("restored");
872
- expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(0);
873
- });
874
-
875
- it("Starts with mismatching transmit power, failure does not present start", async () => {
876
- adapter = new EmberAdapter(
877
- DEFAULT_NETWORK_OPTIONS,
878
- DEFAULT_SERIAL_PORT_OPTIONS,
879
- backupPath,
880
- Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 12}),
881
- );
882
- mockEzspSetRadioPower.mockResolvedValueOnce(SLStatus.FAIL);
883
-
884
- const result = adapter.start();
885
-
886
- await vi.advanceTimersByTimeAsync(5000);
887
- await expect(result).resolves.toStrictEqual("resumed");
888
- expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1);
889
- expect(mockEzspSetRadioPower).toHaveBeenCalledWith(12);
890
- expect(loggerSpies.error).toHaveBeenCalledWith("Failed to set transmit power to 12 status=FAIL.", "zh:ember");
891
- });
892
-
893
- it("Fails to start when EZSP layer fails to start", async () => {
894
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
895
-
896
- mockEzspStart.mockResolvedValueOnce(EzspStatus.HOST_FATAL_ERROR);
897
-
898
- const result = adapter.start();
899
-
900
- await expect(result).rejects.toThrow(`Failed to start EZSP layer with status=${EzspStatus[EzspStatus.HOST_FATAL_ERROR]}.`);
901
- });
902
-
903
- it.each([
904
- [
905
- "if NCP has improper stack type",
906
- () => {
907
- mockEzspVersion.mockResolvedValueOnce([14, 1, 123]);
908
- },
909
- "Stack type 1 is not expected!",
910
- ],
911
- [
912
- "if NCP version unsupported",
913
- () => {
914
- mockEzspVersion.mockResolvedValueOnce([12, EZSP_STACK_TYPE_MESH, 123]);
915
- },
916
- `Adapter EZSP protocol version (12) is not supported by Host [${EZSP_MIN_PROTOCOL_VERSION}-${EZSP_PROTOCOL_VERSION}].`,
917
- ],
918
- [
919
- "if NCP has old style version number",
920
- () => {
921
- mockEzspGetVersionStruct.mockResolvedValueOnce([SLStatus.INVALID_PARAMETER, 0]);
922
- },
923
- "NCP has old-style version number. Not supported.",
924
- ],
925
- [
926
- "if network is not valid by end of init sequence",
927
- () => {
928
- mockEzspGetNetworkParameters
929
- .mockResolvedValueOnce([SLStatus.OK, EmberNodeType.COORDINATOR, deepClone(DEFAULT_ADAPTER_NETWORK_PARAMETERS)])
930
- .mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
931
- },
932
- "Failed to get network parameters with status=FAIL.",
933
- ],
934
- [
935
- "if could not set concentrator",
936
- () => {
937
- mockEzspSetConcentrator.mockResolvedValueOnce(SLStatus.FAIL);
938
- },
939
- "[CONCENTRATOR] Failed to set concentrator with status=FAIL.",
940
- ],
941
- [
942
- "if could not add endpoint",
943
- () => {
944
- mockEzspAddEndpoint.mockResolvedValueOnce(SLStatus.FAIL);
945
- },
946
- `Failed to register endpoint '1' with status=FAIL.`,
947
- ],
948
- [
949
- "if could not set multicast table entry",
950
- () => {
951
- mockEzspSetMulticastTableEntry.mockResolvedValueOnce(SLStatus.FAIL);
952
- },
953
- `Failed to register group '0' in multicast table with status=FAIL.`,
954
- ],
955
- [
956
- "if could not set TC key request policy",
957
- () => {
958
- mockEzspSetPolicy
959
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.BINDING_MODIFICATION_POLICY
960
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY
961
- .mockResolvedValueOnce(SLStatus.FAIL); // EzspPolicyId.TC_KEY_REQUEST_POLICY
962
- },
963
- "[INIT TC] Failed to set EzspPolicyId TC_KEY_REQUEST_POLICY to ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY with status=FAIL.",
964
- ],
965
- [
966
- "if could not set app key request policy",
967
- () => {
968
- mockEzspSetPolicy
969
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.BINDING_MODIFICATION_POLICY
970
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY
971
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.TC_KEY_REQUEST_POLICY
972
- .mockResolvedValueOnce(SLStatus.FAIL); // EzspPolicyId.APP_KEY_REQUEST_POLICY
973
- },
974
- "[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to DENY_APP_KEY_REQUESTS with status=FAIL.",
975
- ],
976
- [
977
- "if could not set app key request policy",
978
- () => {
979
- mockEzspSetPolicy
980
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.BINDING_MODIFICATION_POLICY
981
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY
982
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.TC_KEY_REQUEST_POLICY
983
- .mockResolvedValueOnce(SLStatus.OK) // EzspPolicyId.APP_KEY_REQUEST_POLICY
984
- .mockResolvedValueOnce(SLStatus.FAIL); // EzspPolicyId.TRUST_CENTER_POLICY
985
- },
986
- "[INIT TC] Failed to set join policy to USE_PRECONFIGURED_KEY with status=FAIL.",
987
- ],
988
- [
989
- "if could not init network",
990
- () => {
991
- mockEzspNetworkInit.mockResolvedValueOnce(SLStatus.FAIL);
992
- },
993
- "[INIT TC] Failed network init request with status=FAIL.",
994
- ],
995
- [
996
- "if could not export network key",
997
- () => {
998
- mockEzspExportKey.mockResolvedValueOnce([SLStatus.FAIL, Buffer.alloc(16)]);
999
- },
1000
- "[INIT TC] Failed to export Network Key with status=FAIL.",
1001
- ],
1002
- [
1003
- "if could not leave network",
1004
- () => {
1005
- // force leave code path
1006
- mockEzspGetNetworkParameters.mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
1007
- mockEzspLeaveNetwork.mockResolvedValueOnce(SLStatus.FAIL);
1008
- },
1009
- "[INIT TC] Failed leave network request with status=FAIL.",
1010
- ],
1011
- [
1012
- "if form could not set NWK frame counter",
1013
- () => {
1014
- takeRestoredCodePath();
1015
- mockEzspSetNWKFrameCounter.mockResolvedValueOnce(SLStatus.FAIL);
1016
- },
1017
- "[INIT FORM] Failed to set NWK frame counter with status=FAIL.",
1018
- ],
1019
- // [
1020
- // 'if form could not set TC APS frame counter',
1021
- // () => {
1022
- // takeRestoredCodePath();
1023
- // mockEzspSetAPSFrameCounter.mockResolvedValueOnce(SLStatus.FAIL);
1024
- // },
1025
- // `[INIT FORM] Failed to set TC APS frame counter with status=FAIL.`,
1026
- // ],
1027
- [
1028
- "if form could not set initial security state",
1029
- () => {
1030
- takeResetCodePath();
1031
- mockEzspSetInitialSecurityState.mockResolvedValueOnce(SLStatus.FAIL);
1032
- },
1033
- "[INIT FORM] Failed to set initial security state with status=FAIL.",
1034
- ],
1035
- [
1036
- "if form could not set extended security bitmask",
1037
- () => {
1038
- takeResetCodePath();
1039
- mockEzspSetExtendedSecurityBitmask.mockResolvedValueOnce(SLStatus.FAIL);
1040
- },
1041
- "[INIT FORM] Failed to set extended security bitmask to 272 with status=FAIL.",
1042
- ],
1043
- [
1044
- "if could not form network",
1045
- () => {
1046
- takeResetCodePath();
1047
- mockEzspFormNetwork.mockResolvedValueOnce(SLStatus.FAIL);
1048
- },
1049
- "[INIT FORM] Failed form network request with status=FAIL.",
1050
- ],
1051
- [
1052
- "if backup corrupted",
1053
- () => {
1054
- writeFileSync(backupPath, "abcd");
1055
- },
1056
- "[BACKUP] Coordinator backup is corrupted.",
1057
- ],
1058
- [
1059
- "if backup unsupported",
1060
- () => {
1061
- const customBackup = deepClone(DEFAULT_BACKUP);
1062
- // @ts-expect-error mock override
1063
- customBackup.metadata.version = 2;
1064
-
1065
- writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1066
- },
1067
- "[BACKUP] Unsupported open coordinator backup version (version=2).",
1068
- ],
1069
- [
1070
- "if backup not EmberZNet stack specific",
1071
- () => {
1072
- const customBackup = deepClone(DEFAULT_BACKUP);
1073
- customBackup.stack_specific!.ezsp = undefined;
1074
-
1075
- writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1076
- },
1077
- "[BACKUP] Current backup file is not for EmberZNet stack.",
1078
- ],
1079
- [
1080
- "if backup not EmberZNet EZSP version",
1081
- () => {
1082
- const customBackup = deepClone(DEFAULT_BACKUP);
1083
- customBackup.metadata.internal.ezspVersion = undefined;
1084
-
1085
- writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1086
- },
1087
- "[BACKUP] Current backup file is not for EmberZNet stack.",
1088
- ],
1089
- [
1090
- "if backup unknown format",
1091
- () => {
1092
- const customBackup = deepClone(DEFAULT_BACKUP);
1093
- // @ts-expect-error mock override
1094
- customBackup.metadata.format = "unknown";
1095
-
1096
- writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1097
- },
1098
- "[BACKUP] Unknown backup format.",
1099
- ],
1100
- ])("Fails to start %s", async (_reason, setup, error) => {
1101
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1102
-
1103
- setup();
1104
-
1105
- const result = defuseRejection(adapter.start());
1106
-
1107
- await vi.advanceTimersByTimeAsync(5000);
1108
- await expect(result).rejects.toThrow(error);
1109
- });
1110
-
1111
- it("Warns if NCP has non-GA firmware", async () => {
1112
- const type: EmberVersionType = EmberVersionType.ALPHA_1;
1113
-
1114
- mockEzspGetVersionStruct.mockResolvedValueOnce([
1115
- SLStatus.OK,
1116
- {
1117
- build: 135,
1118
- major: 8,
1119
- minor: 0,
1120
- patch: 0,
1121
- special: 0,
1122
- type,
1123
- } as EmberVersion,
1124
- ]);
1125
-
1126
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1127
-
1128
- const result = adapter.start();
1129
-
1130
- await vi.advanceTimersByTimeAsync(5000);
1131
- await expect(result).resolves.toStrictEqual("resumed");
1132
-
1133
- expect(loggerSpies.warning).toHaveBeenCalledWith(`Adapter is running a non-GA version (${EmberVersionType[type]}).`, "zh:ember");
1134
- });
1135
-
1136
- it("Switches EZSP protocol when supported", async () => {
1137
- mockEzspVersion.mockResolvedValueOnce([EZSP_MIN_PROTOCOL_VERSION, EZSP_STACK_TYPE_MESH, 123]);
1138
-
1139
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1140
-
1141
- const result = adapter.start();
1142
-
1143
- await vi.advanceTimersByTimeAsync(5000);
1144
- await expect(result).resolves.toStrictEqual("resumed");
1145
- expect(mockEzspVersion).toHaveBeenNthCalledWith(1, EZSP_PROTOCOL_VERSION);
1146
- expect(mockEzspVersion).toHaveBeenNthCalledWith(2, EZSP_MIN_PROTOCOL_VERSION);
1147
- expect(mockEzspSetProtocolVersion).toHaveBeenCalledWith(EZSP_MIN_PROTOCOL_VERSION);
1148
- });
1149
-
1150
- it("Logs failed set config value on start", async () => {
1151
- mockEzspSetConfigurationValue.mockResolvedValueOnce(SLStatus.ALLOCATION_FAILED);
1152
-
1153
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1154
-
1155
- const result = adapter.start();
1156
-
1157
- await vi.advanceTimersByTimeAsync(5000);
1158
- await expect(result).resolves.toStrictEqual("resumed");
1159
-
1160
- expect(loggerSpies.info).toHaveBeenCalledWith(
1161
- `[EzspConfigId] Failed to SET '${EzspConfigId[EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE]}' TO '2' with status=${SLStatus[SLStatus.ALLOCATION_FAILED]}. Firmware value will be used instead.`,
1162
- "zh:ember",
1163
- );
1164
- });
1165
-
1166
- it("Starts and skips adding endpoint if already present", async () => {
1167
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1168
-
1169
- mockEzspGetEndpointFlags
1170
- .mockResolvedValueOnce([SLStatus.NOT_FOUND, EzspEndpointFlag.DISABLED])
1171
- .mockResolvedValueOnce([SLStatus.OK, EzspEndpointFlag.ENABLED]); // mock GP already registered
1172
-
1173
- const result = adapter.start();
1174
-
1175
- await vi.advanceTimersByTimeAsync(5000);
1176
- await expect(result).resolves.toStrictEqual("resumed");
1177
- expect(mockEzspAddEndpoint).toHaveBeenCalledTimes(1);
1178
- const ep = FIXED_ENDPOINTS[0];
1179
- expect(mockEzspAddEndpoint).toHaveBeenCalledWith(
1180
- ep.endpoint,
1181
- ep.profileId,
1182
- ep.deviceId,
1183
- ep.deviceVersion,
1184
- ep.inClusterList.slice(), // copy
1185
- ep.outClusterList.slice(), // copy
1186
- );
1187
- });
1188
-
1189
- it("Starts and detects when network key frame counter will soon wrap to 0", async () => {
1190
- const customBackup = deepClone(DEFAULT_BACKUP);
1191
- customBackup.network_key.frame_counter = 0xfeeeeeef;
1192
-
1193
- writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1194
-
1195
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1196
- const result = adapter.start();
1197
-
1198
- await vi.advanceTimersByTimeAsync(5000);
1199
- await expect(result).resolves.toStrictEqual("resumed");
1200
- expect(logger.warning).toHaveBeenCalledWith(
1201
- "[INIT TC] Network key frame counter is reaching its limit. A new network key will have to be instaured soon.",
1202
- "zh:ember",
1203
- );
1204
- });
1205
-
1206
- it("Starts and soft-fails if unable to clear key table", async () => {
1207
- takeResetCodePath();
1208
- mockEzspClearKeyTable.mockResolvedValueOnce(SLStatus.FAIL);
1209
-
1210
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1211
- const result = adapter.start();
1212
-
1213
- await vi.advanceTimersByTimeAsync(5000);
1214
- await expect(result).resolves.toStrictEqual("reset");
1215
- expect(loggerSpies.error).toHaveBeenCalledWith("[INIT FORM] Failed to clear key table with status=FAIL.", "zh:ember");
1216
- });
1217
-
1218
- it("Starts but ignores backup if unsupported version", async () => {
1219
- const customBackup = deepClone(DEFAULT_BACKUP);
1220
- customBackup.metadata.internal.ezspVersion = 11;
1221
-
1222
- writeFileSync(backupPath, JSON.stringify(customBackup, undefined, 2));
1223
-
1224
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1225
- const result = adapter.start();
1226
- const old = `${backupPath}.old`;
1227
-
1228
- await vi.advanceTimersByTimeAsync(5000);
1229
- await expect(result).resolves.toStrictEqual("resumed");
1230
- expect(existsSync(old)).toBeTruthy();
1231
- expect(loggerSpies.warning).toHaveBeenCalledWith(
1232
- "[BACKUP] Current backup file is from an unsupported EZSP version. Renaming and ignoring.",
1233
- "zh:ember",
1234
- );
1235
-
1236
- // cleanup
1237
- unlinkSync(old);
1238
- });
1239
-
1240
- describe("When started", () => {
1241
- beforeEach(async () => {
1242
- adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
1243
-
1244
- const result = adapter.start();
1245
-
1246
- await vi.advanceTimersByTimeAsync(5000);
1247
- await result;
1248
-
1249
- // clean slate "post-start"
1250
- clearMocks();
1251
- });
1252
-
1253
- it("Stops Ezsp layer on stop", async () => {
1254
- // @ts-expect-error private
1255
- const ezspStopSpy = vi.spyOn(adapter.ezsp, "stop");
1256
- // @ts-expect-error private
1257
- const ezspRemoveAllListenersSpy = vi.spyOn(adapter.ezsp, "removeAllListeners");
1258
-
1259
- await adapter.stop();
1260
-
1261
- expect(ezspStopSpy).toHaveBeenCalledTimes(1);
1262
- expect(ezspRemoveAllListenersSpy).toHaveBeenCalledTimes(1);
1263
- });
1264
-
1265
- it("Retrieves parameters from cache when cache valid", async () => {
1266
- await expect(adapter.emberGetEui64()).resolves.toStrictEqual(DEFAULT_COORDINATOR_IEEE);
1267
- expect(mockEzspGetEui64).toHaveBeenCalledTimes(0);
1268
-
1269
- await expect(adapter.emberGetPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.panID);
1270
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
1271
-
1272
- await expect(adapter.emberGetExtendedPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.extendedPanID!);
1273
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
1274
-
1275
- await expect(adapter.emberGetRadioChannel()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.channelList[0]);
1276
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
1277
- });
1278
-
1279
- it("Retrieves parameters from NCP when cache invalid", async () => {
1280
- adapter.clearNetworkCache();
1281
- await expect(adapter.emberGetEui64()).resolves.toStrictEqual(DEFAULT_COORDINATOR_IEEE);
1282
- expect(mockEzspGetEui64).toHaveBeenCalledTimes(1);
1283
-
1284
- adapter.clearNetworkCache();
1285
- await expect(adapter.emberGetPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.panID);
1286
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(1);
1287
-
1288
- adapter.clearNetworkCache();
1289
- await expect(adapter.emberGetExtendedPanId()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.extendedPanID!);
1290
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(2);
1291
-
1292
- adapter.clearNetworkCache();
1293
- await expect(adapter.emberGetRadioChannel()).resolves.toStrictEqual(DEFAULT_NETWORK_OPTIONS.channelList[0]);
1294
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(3);
1295
- });
1296
-
1297
- it("Throws when failed to retrieve parameter from NCP", async () => {
1298
- mockEzspGetNetworkParameters
1299
- .mockResolvedValueOnce([SLStatus.FAIL, 0, {}])
1300
- .mockResolvedValueOnce([SLStatus.FAIL, 0, {}])
1301
- .mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
1302
-
1303
- adapter.clearNetworkCache();
1304
-
1305
- const p1 = defuseRejection(adapter.emberGetPanId());
1306
-
1307
- await vi.advanceTimersByTimeAsync(5000);
1308
- await expect(p1).rejects.toThrow("Failed to get PAN ID (via network parameters) with status=FAIL.");
1309
-
1310
- adapter.clearNetworkCache();
1311
-
1312
- const p2 = defuseRejection(adapter.emberGetExtendedPanId());
1313
-
1314
- await vi.advanceTimersByTimeAsync(5000);
1315
- await expect(p2).rejects.toThrow("Failed to get Extended PAN ID (via network parameters) with status=FAIL.");
1316
-
1317
- adapter.clearNetworkCache();
1318
-
1319
- const p3 = defuseRejection(adapter.emberGetRadioChannel());
1320
-
1321
- await vi.advanceTimersByTimeAsync(5000);
1322
- await expect(p3).rejects.toThrow("Failed to get radio channel (via network parameters) with status=FAIL.");
1323
- });
1324
-
1325
- it("Logs stack status change", async () => {
1326
- mockEzspEmitter.emit("stackStatus", SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED);
1327
- await flushPromises();
1328
-
1329
- expect(loggerSpies.debug).toHaveBeenCalledWith(
1330
- `[STACK STATUS] ${SLStatus[SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED]}.`,
1331
- "zh:ember",
1332
- );
1333
- });
1334
-
1335
- it("Handles message delivery failure", async () => {
1336
- let apsFrame: EmberApsFrame = {
1337
- profileId: ZSpec.HA_PROFILE_ID,
1338
- clusterId: Zcl.Clusters.genBasic.ID,
1339
- sourceEndpoint: 1,
1340
- destinationEndpoint: 1,
1341
- options: 0,
1342
- groupId: 0,
1343
- sequence: 0,
1344
- };
1345
-
1346
- mockEzspEmitter.emit("messageSent", SLStatus.ZIGBEE_DELIVERY_FAILED, EmberOutgoingMessageType.BROADCAST, 1234, apsFrame, 1);
1347
- await flushPromises();
1348
-
1349
- expect(loggerSpies.error).toHaveBeenCalledWith(`Delivery of BROADCAST failed for '1234'.`, "zh:ember");
1350
-
1351
- const spyDeliveryFailedFor = vi.spyOn(
1352
- // @ts-expect-error private
1353
- adapter.oneWaitress,
1354
- "deliveryFailedFor",
1355
- );
1356
-
1357
- apsFrame = {
1358
- profileId: ZSpec.HA_PROFILE_ID,
1359
- clusterId: Zcl.Clusters.genBasic.ID,
1360
- sourceEndpoint: 1,
1361
- destinationEndpoint: 1,
1362
- options: 0,
1363
- groupId: 0,
1364
- sequence: 0,
1365
- };
1366
-
1367
- mockEzspEmitter.emit("messageSent", SLStatus.ZIGBEE_DELIVERY_FAILED, EmberOutgoingMessageType.DIRECT, 1234, apsFrame, 1);
1368
- await flushPromises();
1369
-
1370
- expect(spyDeliveryFailedFor).toHaveBeenCalledTimes(1);
1371
- expect(spyDeliveryFailedFor).toHaveBeenCalledWith(1234, apsFrame);
1372
- });
1373
-
1374
- it("Registers message unknown group in multicast table", async () => {
1375
- // @ts-expect-error private
1376
- const tableIdx = adapter.multicastTable.length;
1377
- const apsFrame = {
1378
- profileId: ZSpec.HA_PROFILE_ID,
1379
- clusterId: Zcl.Clusters.genBasic.ID,
1380
- sourceEndpoint: 1,
1381
- destinationEndpoint: 0xff,
1382
- options: 0,
1383
- groupId: 123,
1384
- sequence: 0,
1385
- };
1386
-
1387
- mockEzspEmitter.emit("messageSent", SLStatus.OK, EmberOutgoingMessageType.MULTICAST, 1234, apsFrame, 1);
1388
- await flushPromises();
1389
-
1390
- expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledTimes(1);
1391
- expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledWith(tableIdx, {
1392
- multicastId: 123,
1393
- endpoint: FIXED_ENDPOINTS[0].endpoint,
1394
- networkIndex: FIXED_ENDPOINTS[0].networkIndex,
1395
- } as EmberMulticastTableEntry);
1396
- expect(
1397
- // @ts-expect-error private
1398
- adapter.multicastTable.length,
1399
- ).toStrictEqual(tableIdx + 1);
1400
- });
1401
-
1402
- it("Fails to register message unknown group in multicast table", async () => {
1403
- mockEzspSetMulticastTableEntry.mockResolvedValueOnce(SLStatus.FAIL);
1404
-
1405
- // @ts-expect-error private
1406
- const tableIdx = adapter.multicastTable.length;
1407
- const apsFrame = {
1408
- profileId: ZSpec.HA_PROFILE_ID,
1409
- clusterId: Zcl.Clusters.genBasic.ID,
1410
- sourceEndpoint: 1,
1411
- destinationEndpoint: 0xff,
1412
- options: 0,
1413
- groupId: 123,
1414
- sequence: 0,
1415
- };
1416
-
1417
- mockEzspEmitter.emit("messageSent", SLStatus.OK, EmberOutgoingMessageType.MULTICAST, 1234, apsFrame, 1);
1418
- await flushPromises();
1419
-
1420
- expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledTimes(1);
1421
- expect(mockEzspSetMulticastTableEntry).toHaveBeenCalledWith(tableIdx, {
1422
- multicastId: 123,
1423
- endpoint: FIXED_ENDPOINTS[0].endpoint,
1424
- networkIndex: FIXED_ENDPOINTS[0].networkIndex,
1425
- } as EmberMulticastTableEntry);
1426
- expect(
1427
- // @ts-expect-error private
1428
- adapter.multicastTable.length,
1429
- ).toStrictEqual(tableIdx); // not increased, entry was removed
1430
- });
1431
-
1432
- it("Emits network address event on ZDO NETWORK_ADDRESS_RESPONSE", async () => {
1433
- const spyResolveZDO = vi.spyOn(
1434
- // @ts-expect-error private
1435
- adapter.oneWaitress,
1436
- "resolveZDO",
1437
- );
1438
- const spyEmit = vi.spyOn(adapter, "emit");
1439
- const sender = 1234;
1440
- const apsFrame: EmberApsFrame = {
1441
- profileId: Zdo.ZDO_PROFILE_ID,
1442
- clusterId: Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE,
1443
- sourceEndpoint: Zdo.ZDO_ENDPOINT,
1444
- destinationEndpoint: Zdo.ZDO_ENDPOINT,
1445
- options: 0,
1446
- groupId: 0,
1447
- sequence: 0,
1448
- };
1449
-
1450
- mockEzspEmitter.emit(
1451
- "zdoResponse",
1452
- apsFrame,
1453
- sender,
1454
- Buffer.from([1, Zdo.Status.SUCCESS, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x11, 0x22, 0x33, 0xd2, 0x04]),
1455
- );
1456
- await flushPromises();
1457
-
1458
- const zdoResponse = [
1459
- Zdo.Status.SUCCESS,
1460
- {
1461
- eui64: "0x332211eeddccbbaa",
1462
- nwkAddress: sender,
1463
- startIndex: 0,
1464
- assocDevList: [],
1465
- } as ZdoTypes.NetworkAddressResponse,
1466
- ];
1467
-
1468
- expect(spyResolveZDO).toHaveBeenCalledTimes(1);
1469
- expect(spyResolveZDO).toHaveBeenCalledWith("0x332211eeddccbbaa", apsFrame, zdoResponse);
1470
- expect(spyEmit).toHaveBeenCalledWith("zdoResponse", Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE, zdoResponse);
1471
- });
1472
-
1473
- it("Emits device announce event on ZDO END_DEVICE_ANNOUNCE", async () => {
1474
- const spyResolveZDO = vi.spyOn(
1475
- // @ts-expect-error private
1476
- adapter.oneWaitress,
1477
- "resolveZDO",
1478
- );
1479
- const spyEmit = vi.spyOn(adapter, "emit");
1480
- const sender = 1234;
1481
- const apsFrame: EmberApsFrame = {
1482
- profileId: Zdo.ZDO_PROFILE_ID,
1483
- clusterId: Zdo.ClusterId.END_DEVICE_ANNOUNCE,
1484
- sourceEndpoint: Zdo.ZDO_ENDPOINT,
1485
- destinationEndpoint: Zdo.ZDO_ENDPOINT,
1486
- options: 0,
1487
- groupId: 0,
1488
- sequence: 0,
1489
- };
1490
-
1491
- mockEzspEmitter.emit("zdoResponse", apsFrame, sender, Buffer.from([1, 0xd2, 0x04, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x11, 0x22, 0x33, 6]));
1492
-
1493
- await flushPromises();
1494
-
1495
- const zdoResponse = [
1496
- Zdo.Status.SUCCESS,
1497
- {
1498
- nwkAddress: sender,
1499
- eui64: "0x332211eeddccbbaa",
1500
- capabilities: {
1501
- alternatePANCoordinator: 0,
1502
- deviceType: 1,
1503
- powerSource: 1,
1504
- rxOnWhenIdle: 0,
1505
- reserved1: 0,
1506
- reserved2: 0,
1507
- securityCapability: 0,
1508
- allocateAddress: 0,
1509
- },
1510
- } as ZdoTypes.EndDeviceAnnounce,
1511
- ];
1512
- expect(spyResolveZDO).toHaveBeenCalledTimes(1);
1513
- expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
1514
- expect(spyEmit).toHaveBeenCalledWith("zdoResponse", Zdo.ClusterId.END_DEVICE_ANNOUNCE, zdoResponse);
1515
- });
1516
-
1517
- it("Emits ZCL payload on incoming message", async () => {
1518
- const spyResolveZCL = vi.spyOn(
1519
- // @ts-expect-error private
1520
- adapter.oneWaitress,
1521
- "resolveZCL",
1522
- );
1523
- const spyEmit = vi.spyOn(adapter, "emit");
1524
- const sender = 1234;
1525
- const apsFrame: EmberApsFrame = {
1526
- profileId: ZSpec.HA_PROFILE_ID,
1527
- clusterId: Zcl.Clusters.genBasic.ID,
1528
- sourceEndpoint: 2,
1529
- destinationEndpoint: 1,
1530
- options: 0,
1531
- groupId: 0,
1532
- sequence: 0,
1533
- };
1534
- const lastHopLqi = 252;
1535
- // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
1536
- const messageContents = Buffer.from("1803010000002003", "hex");
1537
-
1538
- mockEzspEmitter.emit("incomingMessage", EmberIncomingMessageType.UNICAST, apsFrame, lastHopLqi, sender, messageContents);
1539
- await flushPromises();
1540
-
1541
- const payload: ZclPayload = {
1542
- clusterID: apsFrame.clusterId,
1543
- header: Zcl.Header.fromBuffer(messageContents),
1544
- address: sender,
1545
- data: messageContents,
1546
- endpoint: apsFrame.sourceEndpoint,
1547
- linkquality: lastHopLqi,
1548
- groupID: apsFrame.groupId,
1549
- wasBroadcast: false,
1550
- destinationEndpoint: apsFrame.destinationEndpoint,
1551
- };
1552
-
1553
- expect(spyResolveZCL).toHaveBeenCalledTimes(1);
1554
- expect(spyResolveZCL).toHaveBeenCalledWith(payload);
1555
- expect(spyEmit).toHaveBeenCalledWith("zclPayload", payload);
1556
- });
1557
-
1558
- it("Emits ZCL payload on touchlink message", async () => {
1559
- const spyResolveZCL = vi.spyOn(
1560
- // @ts-expect-error private
1561
- adapter.oneWaitress,
1562
- "resolveZCL",
1563
- );
1564
- const spyEmit = vi.spyOn(adapter, "emit");
1565
- const sourcePanId: PanId = 0x1234;
1566
- const sourceAddress: Eui64 = "0x1122334455aabbcc";
1567
- const lastHopLqi = 252;
1568
- const groupId: number = 0;
1569
- const messageContents = Buffer.from("1803010000002003", "hex");
1570
-
1571
- mockEzspEmitter.emit("touchlinkMessage", sourcePanId, sourceAddress, groupId, lastHopLqi, messageContents);
1572
- await flushPromises();
1573
-
1574
- const payload: ZclPayload = {
1575
- clusterID: Zcl.Clusters.touchlink.ID,
1576
- header: Zcl.Header.fromBuffer(messageContents),
1577
- address: sourceAddress,
1578
- data: messageContents,
1579
- endpoint: 1,
1580
- linkquality: lastHopLqi,
1581
- groupID: groupId,
1582
- wasBroadcast: true,
1583
- destinationEndpoint: FIXED_ENDPOINTS[0].endpoint,
1584
- };
1585
-
1586
- expect(spyResolveZCL).toHaveBeenCalledTimes(1);
1587
- expect(spyResolveZCL).toHaveBeenCalledWith(payload);
1588
- expect(spyEmit).toHaveBeenCalledWith("zclPayload", payload);
1589
- });
1590
-
1591
- it("Emits ZCL payload on greenpower message", async () => {
1592
- const spyResolveZCL = vi.spyOn(
1593
- // @ts-expect-error private
1594
- adapter.oneWaitress,
1595
- "resolveZCL",
1596
- );
1597
- const spyEmit = vi.spyOn(adapter, "emit");
1598
- const sourceId: number = 1234;
1599
- const nwkAddress: NodeId = sourceId & 0xffff;
1600
- const gpdLink: number = 123;
1601
- const sequenceNumber: number = 1;
1602
- const commandIdentifier: number = Zcl.Clusters.greenPower.commands.commissioningNotification.ID;
1603
- const frameCounter: number = 102;
1604
- const gpdCommandId: number = 0xe0;
1605
- const gpdCommandPayload = Buffer.from([
1606
- 0x02 /* deviceID */,
1607
- 0x83 /* options */,
1608
- 0xf2 /* extendedOptions */,
1609
- ...[0xf1, 0xec, 0x92, 0xab, 0xff, 0x8f, 0x13, 0x63, 0xe1, 0x46, 0xbe, 0xb5, 0x18, 0xc9, 0x0c, 0xab] /* securityKey */,
1610
- 0xa4,
1611
- 0x46,
1612
- 0xd4,
1613
- 0xd5 /* keyMic */,
1614
- 0xe4,
1615
- 0x04,
1616
- 0x00,
1617
- 0x00 /* outgoingCounter */,
1618
- ]);
1619
- const gpdHeader = Buffer.alloc(15);
1620
- gpdHeader.writeUInt8(0b00000001, 0);
1621
- gpdHeader.writeUInt8(sequenceNumber, 1);
1622
- gpdHeader.writeUInt8(commandIdentifier, 2);
1623
- gpdHeader.writeUInt16LE(0, 3);
1624
- gpdHeader.writeUInt32LE(sourceId, 5);
1625
- gpdHeader.writeUInt32LE(frameCounter, 9);
1626
- gpdHeader.writeUInt8(gpdCommandId, 13);
1627
- gpdHeader.writeUInt8(gpdCommandPayload.length, 14);
1628
- const messageContents = Buffer.concat([gpdHeader, gpdCommandPayload]);
1629
- const apsFrame: EmberApsFrame = {
1630
- profileId: ZSpec.GP_PROFILE_ID,
1631
- clusterId: Zcl.Clusters.greenPower.ID,
1632
- sourceEndpoint: ZSpec.GP_ENDPOINT,
1633
- destinationEndpoint: ZSpec.GP_ENDPOINT,
1634
- options: 0, // not used
1635
- groupId: ZSpec.GP_GROUP_ID,
1636
- sequence: 0, // not used
1637
- };
1638
-
1639
- mockEzspEmitter.emit("incomingMessage", EmberIncomingMessageType.BROADCAST, apsFrame, gpdLink, nwkAddress, messageContents);
1640
- await flushPromises();
1641
-
1642
- const payload: ZclPayload = {
1643
- header: Zcl.Header.fromBuffer(messageContents),
1644
- data: messageContents,
1645
- clusterID: apsFrame.clusterId,
1646
- address: nwkAddress,
1647
- endpoint: apsFrame.sourceEndpoint,
1648
- linkquality: gpdLink,
1649
- groupID: apsFrame.groupId,
1650
- wasBroadcast: true,
1651
- destinationEndpoint: apsFrame.destinationEndpoint,
1652
- };
1653
-
1654
- expect(spyResolveZCL).toHaveBeenCalledTimes(1);
1655
- expect(spyResolveZCL).toHaveBeenCalledWith(payload);
1656
- expect(spyEmit).toHaveBeenCalledWith("zclPayload", payload);
1657
- });
1658
-
1659
- it("Emits device joined on trust center join", async () => {
1660
- const spyEmit = vi.spyOn(adapter, "emit");
1661
- const newNodeId: NodeId = 1234;
1662
- const newNodeEui64: Eui64 = "0x11223344eebbccaa";
1663
- const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
1664
- const policyDecision: EmberJoinDecision = EmberJoinDecision.USE_PRECONFIGURED_KEY;
1665
- const parentOfNewNodeId: NodeId = 4321;
1666
-
1667
- mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1668
- await flushPromises();
1669
-
1670
- expect(spyEmit).toHaveBeenCalledWith("deviceJoined", {
1671
- networkAddress: newNodeId,
1672
- ieeeAddr: newNodeEui64,
1673
- } as DeviceJoinedPayload);
1674
- });
1675
-
1676
- it("Emits device leave on trust center join", async () => {
1677
- const spyEmit = vi.spyOn(adapter, "emit");
1678
- const newNodeId: NodeId = 1234;
1679
- const newNodeEui64: Eui64 = "0x11223344eebbccaa";
1680
- const status: EmberDeviceUpdate = EmberDeviceUpdate.DEVICE_LEFT;
1681
- const policyDecision: EmberJoinDecision = EmberJoinDecision.NO_ACTION;
1682
- const parentOfNewNodeId: NodeId = 0xffff;
1683
-
1684
- mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1685
- await flushPromises();
1686
-
1687
- expect(spyEmit).toHaveBeenCalledWith("deviceLeave", {
1688
- networkAddress: newNodeId,
1689
- ieeeAddr: newNodeEui64,
1690
- } as DeviceLeavePayload);
1691
- });
1692
-
1693
- it("Handles DENY_JOIN on trust center join", async () => {
1694
- const newNodeId: NodeId = 1234;
1695
- const newNodeEui64: Eui64 = "0x11223344eebbccaa";
1696
- const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
1697
- const policyDecision: EmberJoinDecision = EmberJoinDecision.DENY_JOIN;
1698
- const parentOfNewNodeId: NodeId = 4321;
1699
-
1700
- mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1701
- await flushPromises();
1702
-
1703
- expect(loggerSpies.warning).toHaveBeenCalledWith(
1704
- `[TRUST CENTER] Device ${newNodeId}:${newNodeEui64} was denied joining via ${parentOfNewNodeId}.`,
1705
- "zh:ember",
1706
- );
1707
- });
1708
-
1709
- it("Handles device join workaround requiring specific manufacturer code", async () => {
1710
- const spyEmit = vi.spyOn(adapter, "emit");
1711
- const newNodeId: NodeId = 1234;
1712
- const newNodeEui64: Eui64 = "0x54ef44ffeebbccaa";
1713
- const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
1714
- const policyDecision: EmberJoinDecision = EmberJoinDecision.USE_PRECONFIGURED_KEY;
1715
- const parentOfNewNodeId: NodeId = 4321;
1716
-
1717
- mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
1718
- await flushPromises();
1719
-
1720
- expect(spyEmit).toHaveBeenCalledWith("deviceJoined", {
1721
- networkAddress: newNodeId,
1722
- ieeeAddr: newNodeEui64,
1723
- } as DeviceJoinedPayload);
1724
- expect(mockEzspSetManufacturerCode).toHaveBeenCalledWith(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
1725
- expect(mockManufCode).toStrictEqual(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
1726
- });
1727
-
1728
- it("Triggers watchdog counters", async () => {
1729
- await vi.advanceTimersByTimeAsync(3610000);
1730
- expect(mockEzspReadAndClearCounters).toHaveBeenCalledTimes(1);
1731
- expect(loggerSpies.info).toHaveBeenCalledTimes(2);
1732
- expect(loggerSpies.info.mock.calls[0][0]).toMatch(/[NCP COUNTERS]/);
1733
- expect(loggerSpies.info.mock.calls[1][0]).toMatch(/[ASH COUNTERS]/);
1734
- });
1735
-
1736
- it("Exports link keys", async () => {
1737
- const k1Context: SecManContext = {
1738
- coreKeyType: SecManKeyType.APP_LINK,
1739
- keyIndex: 0,
1740
- derivedType: SecManDerivedKeyType.NONE,
1741
- eui64: "0x1122334455667788",
1742
- multiNetworkIndex: 0,
1743
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1744
- psaKeyAlgPermission: 0,
1745
- };
1746
- const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1747
- const k1Hashed = ZSpec.Utils.aes128MmoHash(k1);
1748
- const k1Metadata: SecManAPSKeyMetadata = {
1749
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1750
- outgoingFrameCounter: 1,
1751
- incomingFrameCounter: 2,
1752
- ttlInSeconds: 0,
1753
- };
1754
- const k2Context: SecManContext = {
1755
- coreKeyType: SecManKeyType.APP_LINK,
1756
- keyIndex: 1,
1757
- derivedType: SecManDerivedKeyType.NONE,
1758
- eui64: "0x2233445566778899",
1759
- multiNetworkIndex: 0,
1760
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1761
- psaKeyAlgPermission: 0,
1762
- };
1763
- const k2 = Buffer.from([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]);
1764
- const k2Hashed = ZSpec.Utils.aes128MmoHash(k2);
1765
- const k2Metadata: SecManAPSKeyMetadata = {
1766
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1767
- outgoingFrameCounter: 10,
1768
- incomingFrameCounter: 20,
1769
- ttlInSeconds: 0,
1770
- };
1771
- const k3Context: SecManContext = {
1772
- coreKeyType: SecManKeyType.APP_LINK,
1773
- keyIndex: 2,
1774
- derivedType: SecManDerivedKeyType.NONE,
1775
- eui64: "0x3344556677889900",
1776
- multiNetworkIndex: 0,
1777
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1778
- psaKeyAlgPermission: 0,
1779
- };
1780
- const k3 = Buffer.from([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]);
1781
- const k3Hashed = ZSpec.Utils.aes128MmoHash(k3);
1782
- const k3Metadata: SecManAPSKeyMetadata = {
1783
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1784
- outgoingFrameCounter: 100,
1785
- incomingFrameCounter: 200,
1786
- ttlInSeconds: 0,
1787
- };
1788
-
1789
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
1790
- mockEzspExportLinkKeyByIndex
1791
- .mockResolvedValueOnce([SLStatus.OK, k1Context, {contents: k1} as SecManKey, k1Metadata])
1792
- .mockResolvedValueOnce([SLStatus.OK, k2Context, {contents: k2} as SecManKey, k2Metadata])
1793
- .mockResolvedValueOnce([SLStatus.OK, k3Context, {contents: k3} as SecManKey, k3Metadata]);
1794
-
1795
- const keys = await adapter.exportLinkKeys();
1796
-
1797
- expect(mockEzspExportLinkKeyByIndex).toHaveBeenCalledTimes(3);
1798
- expect(keys).toStrictEqual([
1799
- {
1800
- deviceEui64: k1Context.eui64,
1801
- key: {contents: k1Hashed},
1802
- outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
1803
- incomingFrameCounter: k1Metadata.incomingFrameCounter,
1804
- } as LinkKeyBackupData,
1805
- {
1806
- deviceEui64: k2Context.eui64,
1807
- key: {contents: k2Hashed},
1808
- outgoingFrameCounter: k2Metadata.outgoingFrameCounter,
1809
- incomingFrameCounter: k2Metadata.incomingFrameCounter,
1810
- } as LinkKeyBackupData,
1811
- {
1812
- deviceEui64: k3Context.eui64,
1813
- key: {contents: k3Hashed},
1814
- outgoingFrameCounter: k3Metadata.outgoingFrameCounter,
1815
- incomingFrameCounter: k3Metadata.incomingFrameCounter,
1816
- } as LinkKeyBackupData,
1817
- ]);
1818
- });
1819
-
1820
- it("Exports zero link keys", async () => {
1821
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 0]);
1822
- const keys = await adapter.exportLinkKeys();
1823
-
1824
- expect(keys).toStrictEqual([]);
1825
- });
1826
-
1827
- it("Fails to export link keys due to failed table size retrieval", async () => {
1828
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.FAIL, 0]);
1829
-
1830
- await expect(adapter.exportLinkKeys()).rejects.toThrow("[BACKUP] Failed to retrieve key table size from NCP with status=FAIL.");
1831
- });
1832
-
1833
- it("Imports link keys", async () => {
1834
- const k1Context: SecManContext = {
1835
- coreKeyType: SecManKeyType.APP_LINK,
1836
- keyIndex: 0,
1837
- derivedType: SecManDerivedKeyType.NONE,
1838
- eui64: "0x1122334455667788",
1839
- multiNetworkIndex: 0,
1840
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1841
- psaKeyAlgPermission: 0,
1842
- };
1843
- const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1844
- const k1Metadata: SecManAPSKeyMetadata = {
1845
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1846
- outgoingFrameCounter: 1,
1847
- incomingFrameCounter: 2,
1848
- ttlInSeconds: 0,
1849
- };
1850
- const k2Context: SecManContext = {
1851
- coreKeyType: SecManKeyType.APP_LINK,
1852
- keyIndex: 1,
1853
- derivedType: SecManDerivedKeyType.NONE,
1854
- eui64: "0x2233445566778899",
1855
- multiNetworkIndex: 0,
1856
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1857
- psaKeyAlgPermission: 0,
1858
- };
1859
- const k2 = Buffer.from([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]);
1860
- const k2Metadata: SecManAPSKeyMetadata = {
1861
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1862
- outgoingFrameCounter: 10,
1863
- incomingFrameCounter: 20,
1864
- ttlInSeconds: 0,
1865
- };
1866
- const k3Context: SecManContext = {
1867
- coreKeyType: SecManKeyType.APP_LINK,
1868
- keyIndex: 2,
1869
- derivedType: SecManDerivedKeyType.NONE,
1870
- eui64: "0x3344556677889900",
1871
- multiNetworkIndex: 0,
1872
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1873
- psaKeyAlgPermission: 0,
1874
- };
1875
- const k3 = Buffer.from([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]);
1876
- const k3Metadata: SecManAPSKeyMetadata = {
1877
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1878
- outgoingFrameCounter: 100,
1879
- incomingFrameCounter: 200,
1880
- ttlInSeconds: 0,
1881
- };
1882
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 4]);
1883
- mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.NO_NETWORK);
1884
-
1885
- await adapter.importLinkKeys([
1886
- {
1887
- deviceEui64: k1Context.eui64,
1888
- key: {contents: k1},
1889
- outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
1890
- incomingFrameCounter: k1Metadata.incomingFrameCounter,
1891
- },
1892
- {
1893
- deviceEui64: k2Context.eui64,
1894
- key: {contents: k2},
1895
- outgoingFrameCounter: k2Metadata.outgoingFrameCounter,
1896
- incomingFrameCounter: k2Metadata.incomingFrameCounter,
1897
- },
1898
- {
1899
- deviceEui64: k3Context.eui64,
1900
- key: {contents: k3},
1901
- outgoingFrameCounter: k3Metadata.outgoingFrameCounter,
1902
- incomingFrameCounter: k3Metadata.incomingFrameCounter,
1903
- },
1904
- ]);
1905
-
1906
- expect(mockEzspImportLinkKey).toHaveBeenCalledTimes(3);
1907
- expect(mockEzspEraseKeyTableEntry).toHaveBeenCalledTimes(1);
1908
- });
1909
-
1910
- it("Imports zero link keys", async () => {
1911
- await expect(adapter.importLinkKeys([])).resolves.toStrictEqual(undefined);
1912
- });
1913
-
1914
- it("Failed to import link keys due to failed table size retrieval", async () => {
1915
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.FAIL, 0]);
1916
-
1917
- await expect(
1918
- adapter.importLinkKeys([
1919
- // @ts-expect-error mock, unnecessary
1920
- {},
1921
- ]),
1922
- ).rejects.toThrow("[BACKUP] Failed to retrieve key table size from NCP with status=FAIL.");
1923
- });
1924
-
1925
- it("Failed to import link keys due to insufficient table size", async () => {
1926
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 0]);
1927
-
1928
- await expect(
1929
- adapter.importLinkKeys([
1930
- // @ts-expect-error mock, unnecessary
1931
- {},
1932
- ]),
1933
- ).rejects.toThrow("[BACKUP] Current key table of 0 is too small to import backup of 1!");
1934
- });
1935
-
1936
- it("Failed to import link keys due to improper network state", async () => {
1937
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
1938
- mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.JOINED_NETWORK);
1939
-
1940
- await expect(
1941
- adapter.importLinkKeys([
1942
- // @ts-expect-error mock, unnecessary
1943
- {},
1944
- ]),
1945
- ).rejects.toThrow(
1946
- `[BACKUP] Cannot import TC data while network is up, networkStatus=${EmberNetworkStatus[EmberNetworkStatus.JOINED_NETWORK]}.`,
1947
- );
1948
- });
1949
-
1950
- it("Failed to import link keys due to failed key set", async () => {
1951
- const k1Context: SecManContext = {
1952
- coreKeyType: SecManKeyType.APP_LINK,
1953
- keyIndex: 0,
1954
- derivedType: SecManDerivedKeyType.NONE,
1955
- eui64: "0x1122334455667788",
1956
- multiNetworkIndex: 0,
1957
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1958
- psaKeyAlgPermission: 0,
1959
- };
1960
- const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1961
- const k1Metadata: SecManAPSKeyMetadata = {
1962
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1963
- outgoingFrameCounter: 1,
1964
- incomingFrameCounter: 2,
1965
- ttlInSeconds: 0,
1966
- };
1967
-
1968
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
1969
- mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.NO_NETWORK);
1970
- mockEzspImportLinkKey.mockResolvedValueOnce(SLStatus.FAIL);
1971
-
1972
- await expect(
1973
- adapter.importLinkKeys([
1974
- {
1975
- deviceEui64: k1Context.eui64,
1976
- key: {contents: k1},
1977
- outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
1978
- incomingFrameCounter: k1Metadata.incomingFrameCounter,
1979
- },
1980
- ]),
1981
- ).rejects.toThrow("[BACKUP] Failed to set key table entry at index 0 with status=FAIL.");
1982
- });
1983
-
1984
- it("Failed to import link keys due to failed key erase", async () => {
1985
- const k1Context: SecManContext = {
1986
- coreKeyType: SecManKeyType.APP_LINK,
1987
- keyIndex: 0,
1988
- derivedType: SecManDerivedKeyType.NONE,
1989
- eui64: "0x1122334455667788",
1990
- multiNetworkIndex: 0,
1991
- flags: SecManFlag.EUI_IS_VALID | SecManFlag.KEY_INDEX_IS_VALID,
1992
- psaKeyAlgPermission: 0,
1993
- };
1994
- const k1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
1995
- const k1Metadata: SecManAPSKeyMetadata = {
1996
- bitmask: EmberKeyStructBitmask.HAS_INCOMING_FRAME_COUNTER | EmberKeyStructBitmask.HAS_OUTGOING_FRAME_COUNTER,
1997
- outgoingFrameCounter: 1,
1998
- incomingFrameCounter: 2,
1999
- ttlInSeconds: 0,
2000
- };
2001
-
2002
- mockEzspGetConfigurationValue.mockResolvedValueOnce([SLStatus.OK, 3]);
2003
- mockEzspNetworkState.mockResolvedValueOnce(EmberNetworkStatus.NO_NETWORK);
2004
- mockEzspEraseKeyTableEntry.mockResolvedValueOnce(SLStatus.FAIL);
2005
-
2006
- await expect(
2007
- adapter.importLinkKeys([
2008
- {
2009
- deviceEui64: k1Context.eui64,
2010
- key: {contents: k1},
2011
- outgoingFrameCounter: k1Metadata.outgoingFrameCounter,
2012
- incomingFrameCounter: k1Metadata.incomingFrameCounter,
2013
- },
2014
- ]),
2015
- ).rejects.toThrow("[BACKUP] Failed to erase key table entry at index 1 with status=FAIL.");
2016
- });
2017
-
2018
- it("Broadcasts network key update", async () => {
2019
- const p = adapter.broadcastNetworkKeyUpdate();
2020
-
2021
- await vi.advanceTimersByTimeAsync(100000);
2022
- await expect(p).resolves.toStrictEqual(undefined);
2023
- expect(mockEzspBroadcastNextNetworkKey).toHaveBeenCalledTimes(1);
2024
- expect(mockEzspBroadcastNetworkKeySwitch).toHaveBeenCalledTimes(1);
2025
- });
2026
-
2027
- it("Fails to broadcast network key update due to failed next key broadcast", async () => {
2028
- mockEzspBroadcastNextNetworkKey.mockResolvedValueOnce(SLStatus.FAIL);
2029
-
2030
- const p = defuseRejection(adapter.broadcastNetworkKeyUpdate());
2031
-
2032
- await vi.advanceTimersByTimeAsync(100000);
2033
- await expect(p).rejects.toThrow("[TRUST CENTER] Failed to broadcast next network key with status=FAIL.");
2034
- expect(mockEzspBroadcastNextNetworkKey).toHaveBeenCalledTimes(1);
2035
- expect(mockEzspBroadcastNetworkKeySwitch).toHaveBeenCalledTimes(0);
2036
- });
2037
-
2038
- it("Fails to broadcast network key update due to failed switch broadcast", async () => {
2039
- mockEzspBroadcastNetworkKeySwitch.mockResolvedValueOnce(SLStatus.FAIL);
2040
-
2041
- const p = defuseRejection(adapter.broadcastNetworkKeyUpdate());
2042
-
2043
- await vi.advanceTimersByTimeAsync(100000);
2044
- await expect(p).rejects.toThrow("[TRUST CENTER] Failed to broadcast network key switch with status=FAIL.");
2045
- expect(mockEzspBroadcastNextNetworkKey).toHaveBeenCalledTimes(1);
2046
- expect(mockEzspBroadcastNetworkKeySwitch).toHaveBeenCalledTimes(1);
2047
- });
2048
-
2049
- it("Handles NCP needing reset & init", async () => {
2050
- const spyEmit = vi.spyOn(adapter, "emit");
2051
-
2052
- mockEzspEmitter.emit("ncpNeedsResetAndInit", EzspStatus.ERROR_SERIAL_INIT);
2053
- await vi.advanceTimersByTimeAsync(5000);
2054
-
2055
- expect(spyEmit).toHaveBeenCalledTimes(1);
2056
- expect(spyEmit).toHaveBeenCalledWith("disconnected");
2057
- });
2058
-
2059
- it("Emits adapter disconnected when NCP needs reset & init but queue is too high", async () => {
2060
- vi.spyOn(
2061
- // @ts-expect-error private
2062
- adapter.queue,
2063
- "count",
2064
- ).mockReturnValueOnce(999);
2065
- const spyEmit = vi.spyOn(adapter, "emit");
2066
-
2067
- mockEzspEmitter.emit("ncpNeedsResetAndInit", EzspStatus.ERROR_SERIAL_INIT);
2068
- await flushPromises();
2069
-
2070
- expect(spyEmit).toHaveBeenCalledWith("disconnected");
2071
- });
2072
-
2073
- it("Emits adapter disconnected when failed to reset & init NCP", async () => {
2074
- vi.spyOn(adapter, "stop").mockRejectedValueOnce(new Error("mock error"));
2075
- const spyEmit = vi.spyOn(adapter, "emit");
2076
-
2077
- mockEzspEmitter.emit("ncpNeedsResetAndInit", EzspStatus.ERROR_SERIAL_INIT);
2078
- await flushPromises();
2079
-
2080
- expect(spyEmit).toHaveBeenCalledWith("disconnected");
2081
- });
2082
-
2083
- it("Handles channel changed stack status", async () => {
2084
- mockEzspEmitter.emit("stackStatus", SLStatus.ZIGBEE_CHANNEL_CHANGED);
2085
- await flushPromises();
2086
- expect(loggerSpies.info).toHaveBeenCalledWith("[STACK STATUS] Channel changed.", "zh:ember");
2087
- });
2088
-
2089
- it.each([
2090
- ["getCoordinatorIEEE", []],
2091
- ["getNetworkParameters", []],
2092
- ["permitJoin", [250, 1234]],
2093
- ["permitJoin", [250]],
2094
- [
2095
- "sendZclFrameToEndpoint",
2096
- [
2097
- "0x1122334455667788",
2098
- 1234,
2099
- 1,
2100
- Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {}),
2101
- 10000,
2102
- true,
2103
- false,
2104
- 1,
2105
- ],
2106
- ],
2107
- [
2108
- "sendZclFrameToGroup",
2109
- [32, Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {}), 1],
2110
- ],
2111
- [
2112
- "sendZclFrameToAll",
2113
- [
2114
- 1,
2115
- Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {}),
2116
- 1,
2117
- ZSpec.BroadcastAddress.DEFAULT,
2118
- ],
2119
- ],
2120
- ])("Adapter impl: throws when using non-InterPAN function %s while in InterPAN mode", async (funcName, args) => {
2121
- await adapter.setChannelInterPAN(15);
2122
-
2123
- await expect(
2124
- // @ts-expect-error mock
2125
- adapter[funcName](...args),
2126
- ).rejects.toThrow("[INTERPAN MODE] Cannot execute non-InterPAN commands.");
2127
- });
2128
-
2129
- it("Adapter impl: getCoordinatorIEEE", async () => {
2130
- await expect(adapter.getCoordinatorIEEE()).resolves.toStrictEqual(DEFAULT_COORDINATOR_IEEE);
2131
- });
2132
-
2133
- it("Adapter impl: getCoordinatorVersion", async () => {
2134
- await expect(adapter.getCoordinatorVersion()).resolves.toStrictEqual({
2135
- type: "EmberZNet",
2136
- meta: {
2137
- ezsp: EZSP_PROTOCOL_VERSION,
2138
- revision: `8.0.0 [${EmberVersionType[EmberVersionType.GA]}]`,
2139
- build: 135,
2140
- major: 8,
2141
- minor: 0,
2142
- patch: 0,
2143
- special: 0,
2144
- type: EmberVersionType.GA,
2145
- },
2146
- } as TsType.CoordinatorVersion);
2147
- });
2148
-
2149
- it("Adapter impl: reset soft", async () => {
2150
- await expect(adapter.reset("soft")).rejects.toThrow(`Not supported 'soft'.`);
2151
- });
2152
-
2153
- it("Adapter impl: reset hard", async () => {
2154
- await expect(adapter.reset("hard")).rejects.toThrow(`Not supported 'hard'.`);
2155
- });
2156
-
2157
- it("Adapter impl: supportsBackup", async () => {
2158
- await expect(adapter.supportsBackup()).resolves.toStrictEqual(true);
2159
- });
2160
-
2161
- it("Adapter impl: backup", async () => {
2162
- await expect(adapter.backup([])).resolves.toStrictEqual({
2163
- networkOptions: {
2164
- panId: DEFAULT_NETWORK_OPTIONS.panID, // uint16_t
2165
- extendedPanId: Buffer.from(DEFAULT_NETWORK_OPTIONS.extendedPanID!),
2166
- channelList: ZSpec.ALL_802_15_4_CHANNELS.slice(),
2167
- networkKey: Buffer.from(DEFAULT_BACKUP.network_key.key, "hex"),
2168
- networkKeyDistribute: false,
2169
- },
2170
- logicalChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
2171
- networkKeyInfo: {
2172
- sequenceNumber: DEFAULT_BACKUP.network_key.sequence_number,
2173
- frameCounter: DEFAULT_BACKUP.network_key.frame_counter,
2174
- },
2175
- securityLevel: SECURITY_LEVEL_Z3,
2176
- networkUpdateId: 0,
2177
- coordinatorIeeeAddress: Buffer.from(DEFAULT_BACKUP.coordinator_ieee, "hex"),
2178
- devices: [],
2179
- ezsp: {
2180
- version: EZSP_PROTOCOL_VERSION,
2181
- hashed_tclk: Buffer.from(DEFAULT_BACKUP.stack_specific!.ezsp!.hashed_tclk!, "hex"),
2182
- },
2183
- } as Backup);
2184
- });
2185
-
2186
- it.each([
2187
- [
2188
- "failed get network parameters",
2189
- () => {
2190
- mockEzspGetNetworkParameters.mockResolvedValueOnce([SLStatus.FAIL, 0, {}]);
2191
- },
2192
- "[BACKUP] Failed to get network parameters with status=FAIL.",
2193
- ],
2194
- [
2195
- "failed get network key info",
2196
- () => {
2197
- mockEzspGetNetworkKeyInfo.mockResolvedValueOnce([SLStatus.FAIL, {}]);
2198
- },
2199
- "[BACKUP] Failed to get network keys info with status=FAIL.",
2200
- ],
2201
- // [
2202
- // 'failed get TC APS key info',
2203
- // () => {
2204
- // mockEzspGetNetworkKeyInfo.mockResolvedValueOnce([SLStatus.FAIL, {}]);
2205
- // },
2206
- // `[BACKUP] Failed to get TC APS key info with status=FAIL.`,
2207
- // ],
2208
- [
2209
- "no network key set",
2210
- () => {
2211
- mockEzspGetNetworkKeyInfo.mockResolvedValueOnce([
2212
- SLStatus.OK,
2213
- {
2214
- networkKeySet: false,
2215
- alternateNetworkKeySet: false,
2216
- networkKeySequenceNumber: 123,
2217
- altNetworkKeySequenceNumber: 0,
2218
- networkKeyFrameCounter: 456,
2219
- } as SecManNetworkKeyInfo,
2220
- ]);
2221
- },
2222
- "[BACKUP] No network key set.",
2223
- ],
2224
- [
2225
- "failed export TC link key",
2226
- () => {
2227
- mockEzspExportKey.mockResolvedValueOnce([SLStatus.FAIL, {}]);
2228
- },
2229
- "[BACKUP] Failed to export TC Link Key with status=FAIL.",
2230
- ],
2231
- [
2232
- "failed export network key",
2233
- () => {
2234
- mockEzspExportKey
2235
- .mockResolvedValueOnce([
2236
- SLStatus.OK,
2237
- {contents: Buffer.from(DEFAULT_BACKUP.stack_specific!.ezsp!.hashed_tclk!, "hex")} as SecManKey,
2238
- ])
2239
- .mockResolvedValueOnce([SLStatus.FAIL, {}]);
2240
- },
2241
- "[BACKUP] Failed to export Network Key with status=FAIL.",
2242
- ],
2243
- ])("Adapter impl: throws when backup fails due to %s", async (_command, setup, error) => {
2244
- setup();
2245
-
2246
- await expect(adapter.backup([])).rejects.toThrow(error);
2247
- });
2248
-
2249
- it("Adapter impl: getNetworkParameters from cache", async () => {
2250
- await expect(adapter.getNetworkParameters()).resolves.toStrictEqual({
2251
- panID: DEFAULT_NETWORK_OPTIONS.panID,
2252
- extendedPanID: ZSpec.Utils.eui64LEBufferToHex(Buffer.from(DEFAULT_NETWORK_OPTIONS.extendedPanID!)),
2253
- channel: DEFAULT_NETWORK_OPTIONS.channelList[0],
2254
- nwkUpdateID: 0,
2255
- } as TsType.NetworkParameters);
2256
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(0);
2257
- });
2258
-
2259
- it("Adapter impl: getNetworkParameters from NCP", async () => {
2260
- adapter.clearNetworkCache();
2261
-
2262
- await expect(adapter.getNetworkParameters()).resolves.toStrictEqual({
2263
- panID: DEFAULT_NETWORK_OPTIONS.panID,
2264
- extendedPanID: ZSpec.Utils.eui64LEBufferToHex(Buffer.from(DEFAULT_NETWORK_OPTIONS.extendedPanID!)),
2265
- channel: DEFAULT_NETWORK_OPTIONS.channelList[0],
2266
- nwkUpdateID: 0,
2267
- } as TsType.NetworkParameters);
2268
- expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(1);
2269
- });
2270
-
2271
- it("Adapter impl: addInstallCode", async () => {
2272
- await expect(
2273
- adapter.addInstallCode("0x1122334455667788", Buffer.from("DD7ED5CDAA8E2C708B67D2B1573DB6843A5F", "hex"), false),
2274
- ).resolves.toStrictEqual(undefined);
2275
- expect(mockEzspImportTransientKey).toHaveBeenCalledTimes(1);
2276
- expect(loggerSpies.debug).toHaveBeenCalledWith(`[ADD INSTALL CODE] Success for '0x1122334455667788'.`, "zh:ember");
2277
- });
2278
-
2279
- it("Adapter impl: throw when addInstallCode fails import transient key", async () => {
2280
- mockEzspImportTransientKey.mockResolvedValueOnce(SLStatus.FAIL);
2281
-
2282
- await expect(adapter.addInstallCode("0x1122334455667788", Buffer.alloc(16), true)).rejects.toThrow(
2283
- `[ADD INSTALL CODE] Failed for '0x1122334455667788' with status=FAIL.`,
2284
- );
2285
- expect(mockEzspImportTransientKey).toHaveBeenCalledTimes(1);
2286
- });
2287
-
2288
- it("Adapter impl: waitFor", () => {
2289
- const waiter = adapter.waitFor(1234, 1, Zcl.FrameType.GLOBAL, Zcl.Direction.CLIENT_TO_SERVER, 10, 0, 1, 15000);
2290
- const spyCancel = vi.spyOn(waiter, "cancel");
2291
-
2292
- expect(typeof waiter.cancel).toStrictEqual("function");
2293
- expect(waiter.promise).toBeDefined();
2294
-
2295
- waiter.cancel();
2296
-
2297
- expect(spyCancel).toHaveReturned();
2298
- });
2299
-
2300
- it("Adapter impl: permitJoin on all", async () => {
2301
- const spyResolveEvent = vi.spyOn(
2302
- // @ts-expect-error private
2303
- adapter.oneWaitress,
2304
- "resolveEvent",
2305
- );
2306
-
2307
- await adapter.permitJoin(250);
2308
- await vi.advanceTimersByTimeAsync(1000);
2309
- expect(mockEzspPermitJoining).toHaveBeenCalledWith(250);
2310
- expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(1);
2311
- expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_OPENED);
2312
-
2313
- await adapter.permitJoin(0);
2314
- await vi.advanceTimersByTimeAsync(1000);
2315
- expect(mockEzspPermitJoining).toHaveBeenCalledWith(0);
2316
- expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(2);
2317
- expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED);
2318
-
2319
- expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(
2320
- 1,
2321
- EzspPolicyId.TRUST_CENTER_POLICY,
2322
- EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS,
2323
- );
2324
- expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(2, EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS);
2325
- });
2326
-
2327
- it("Adapter impl: permitJoin on coordinator", async () => {
2328
- const spyResolveEvent = vi.spyOn(
2329
- // @ts-expect-error private
2330
- adapter.oneWaitress,
2331
- "resolveEvent",
2332
- );
2333
-
2334
- await adapter.permitJoin(250, ZSpec.COORDINATOR_ADDRESS);
2335
- await vi.advanceTimersByTimeAsync(1000);
2336
- expect(mockEzspPermitJoining).toHaveBeenCalledWith(250);
2337
- expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(0);
2338
- expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_OPENED);
2339
-
2340
- await adapter.permitJoin(0, ZSpec.COORDINATOR_ADDRESS);
2341
- await vi.advanceTimersByTimeAsync(1000);
2342
- expect(mockEzspPermitJoining).toHaveBeenCalledWith(0);
2343
- expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(0);
2344
- expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED);
2345
-
2346
- expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(
2347
- 1,
2348
- EzspPolicyId.TRUST_CENTER_POLICY,
2349
- EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS,
2350
- );
2351
- expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(2, EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS);
2352
- });
2353
-
2354
- it("Adapter impl: permitJoin on router", async () => {
2355
- const spyResolveZDO = vi.spyOn(
2356
- // @ts-expect-error private
2357
- adapter.oneWaitress,
2358
- "resolveZDO",
2359
- );
2360
- const sender = 1234;
2361
- const apsFrame: EmberApsFrame = {
2362
- profileId: Zdo.ZDO_PROFILE_ID,
2363
- clusterId: Zdo.ClusterId.PERMIT_JOINING_RESPONSE,
2364
- sourceEndpoint: Zdo.ZDO_ENDPOINT,
2365
- destinationEndpoint: Zdo.ZDO_ENDPOINT,
2366
- options: 0,
2367
- groupId: 0,
2368
- sequence: 0,
2369
- };
2370
- const emitResponse = () => {
2371
- setTimeout(async () => {
2372
- mockEzspEmitter.emit("zdoResponse", apsFrame, sender, Buffer.from([1, Zdo.Status.SUCCESS]));
2373
- await flushPromises();
2374
- }, 300);
2375
-
2376
- return [SLStatus.OK, ++mockAPSSequence];
2377
- };
2378
-
2379
- mockEzspSendUnicast.mockImplementationOnce(emitResponse).mockImplementationOnce(emitResponse);
2380
-
2381
- const zdoResponse = [Zdo.Status.SUCCESS, undefined];
2382
- let p = adapter.permitJoin(250, sender);
2383
- await vi.advanceTimersByTimeAsync(1000);
2384
- await p;
2385
- expect(mockEzspSendUnicast).toHaveBeenCalledTimes(1);
2386
- expect(spyResolveZDO).toHaveBeenCalledTimes(1);
2387
- expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
2388
-
2389
- p = adapter.permitJoin(0, sender);
2390
- await vi.advanceTimersByTimeAsync(1000);
2391
- await p;
2392
- expect(mockEzspSendUnicast).toHaveBeenCalledTimes(2);
2393
- expect(spyResolveZDO).toHaveBeenCalledTimes(2);
2394
- expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
2395
-
2396
- expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(
2397
- 1,
2398
- EzspPolicyId.TRUST_CENTER_POLICY,
2399
- EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS,
2400
- );
2401
- expect(mockEzspSetPolicy).toHaveBeenNthCalledWith(2, EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS);
2402
- });
2403
-
2404
- it("Adapter impl: permitJoin restores temp manufacturer code", async () => {
2405
- const spyResolveEvent = vi.spyOn(
2406
- // @ts-expect-error private
2407
- adapter.oneWaitress,
2408
- "resolveEvent",
2409
- );
2410
-
2411
- const newNodeId: NodeId = 1234;
2412
- const newNodeEui64: Eui64 = "0x54ef44ffeebbccaa";
2413
- const status: EmberDeviceUpdate = EmberDeviceUpdate.STANDARD_SECURITY_UNSECURED_JOIN;
2414
- const policyDecision: EmberJoinDecision = EmberJoinDecision.USE_PRECONFIGURED_KEY;
2415
- const parentOfNewNodeId: NodeId = 4321;
2416
-
2417
- mockEzspEmitter.emit("trustCenterJoin", newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId);
2418
- await flushPromises();
2419
-
2420
- expect(mockEzspSetManufacturerCode).toHaveBeenCalledWith(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
2421
- expect(mockManufCode).toStrictEqual(Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN);
2422
-
2423
- await adapter.permitJoin(0, ZSpec.COORDINATOR_ADDRESS);
2424
- await vi.advanceTimersByTimeAsync(1000);
2425
- expect(mockEzspPermitJoining).toHaveBeenCalledWith(0);
2426
- expect(mockEzspSendBroadcast).toHaveBeenCalledTimes(0);
2427
- expect(spyResolveEvent).toHaveBeenCalledWith(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED);
2428
- expect(mockEzspSetManufacturerCode).toHaveBeenCalledWith(Zcl.ManufacturerCode.SILICON_LABORATORIES);
2429
- expect(mockManufCode).toStrictEqual(Zcl.ManufacturerCode.SILICON_LABORATORIES);
2430
- });
2431
-
2432
- it("Adapter impl: throws when permitJoin request on coordinator fails", async () => {
2433
- mockEzspPermitJoining.mockResolvedValueOnce(SLStatus.FAIL);
2434
-
2435
- await expect(adapter.permitJoin(250, 0)).rejects.toThrow("[ZDO] Failed coordinator permit joining request with status=FAIL.");
2436
- });
2437
-
2438
- it("Adapter impl: throws when permitJoin broadcast request fails", async () => {
2439
- mockEzspSendBroadcast.mockResolvedValueOnce([SLStatus.FAIL, 0]);
2440
-
2441
- await expect(defuseRejection(adapter.permitJoin(250, undefined))).rejects.toThrow(
2442
- "~x~> [ZDO PERMIT_JOINING_REQUEST BROADCAST to=65532 messageTag=1] Failed to send request with status=FAIL.",
2443
- );
2444
- });
2445
-
2446
- it("Adapter impl: resolves undefined when permitJoin on router fails due to failed ZDO status", async () => {
2447
- const spyResolveZDO = vi.spyOn(
2448
- // @ts-expect-error private
2449
- adapter.oneWaitress,
2450
- "resolveZDO",
2451
- );
2452
- const sender = 1234;
2453
- const apsFrame: EmberApsFrame = {
2454
- profileId: Zdo.ZDO_PROFILE_ID,
2455
- clusterId: Zdo.ClusterId.PERMIT_JOINING_RESPONSE,
2456
- sourceEndpoint: Zdo.ZDO_ENDPOINT,
2457
- destinationEndpoint: Zdo.ZDO_ENDPOINT,
2458
- options: 0,
2459
- groupId: 0,
2460
- sequence: 0,
2461
- };
2462
-
2463
- mockEzspEmitter.emit("zdoResponse", apsFrame, sender, Buffer.from([1, Zdo.Status.NOT_AUTHORIZED]));
2464
- await flushPromises();
2465
-
2466
- const zdoResponse = [Zdo.Status.NOT_AUTHORIZED, undefined];
2467
- expect(spyResolveZDO).toHaveBeenCalledTimes(1);
2468
- expect(spyResolveZDO).toHaveBeenCalledWith(sender, apsFrame, zdoResponse);
2469
- });
2470
-
2471
- it("Adapter impl: throws when permitJoin request on router fails", async () => {
2472
- mockEzspSendUnicast.mockResolvedValueOnce([SLStatus.FAIL, 0]);
2473
-
2474
- await expect(adapter.permitJoin(250, 1234)).rejects.toThrow(
2475
- "~x~> [ZDO PERMIT_JOINING_REQUEST UNICAST to=0xFFFFFFFFFFFFFFFF:1234 messageTag=1] Failed to send request with status=FAIL.",
2476
- );
2477
- });
2478
-
2479
- it("Adapter impl: throws when permitJoin fails to import ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY", async () => {
2480
- mockEzspImportTransientKey.mockResolvedValueOnce(SLStatus.FAIL);
2481
-
2482
- await expect(adapter.permitJoin(250)).rejects.toThrow("[ZDO] Failed import transient key with status=FAIL.");
2483
- });
2484
-
2485
- it("Adapter impl: throws when permitJoin fails to set TC policy", async () => {
2486
- mockEzspSetPolicy.mockResolvedValueOnce(SLStatus.FAIL);
2487
-
2488
- await expect(adapter.permitJoin(250)).rejects.toThrow("[ZDO] Failed set join policy with status=FAIL.");
2489
- });
2490
-
2491
- it("Adapter impl: throws when stop permitJoin fails to restore TC policy", async () => {
2492
- mockEzspSetPolicy.mockResolvedValueOnce(SLStatus.FAIL);
2493
-
2494
- await expect(adapter.permitJoin(0)).rejects.toThrow("[ZDO] Failed set join policy with status=FAIL.");
2495
- });
2496
-
2497
- it("Adapter impl: sendZclFrameToEndpoint with command response with fixed source endpoint", async () => {
2498
- const networkAddress: NodeId = 1234;
2499
- const endpoint: number = 1;
2500
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2501
- const zclFrame = Zcl.Frame.create(
2502
- Zcl.FrameType.GLOBAL,
2503
- Zcl.Direction.CLIENT_TO_SERVER,
2504
- true,
2505
- undefined,
2506
- 3,
2507
- "read",
2508
- "genBasic",
2509
- [{attrId: 0}],
2510
- {},
2511
- );
2512
- const apsFrame: EmberApsFrame = {
2513
- profileId: FIXED_ENDPOINTS[0].profileId,
2514
- clusterId: zclFrame.cluster.ID,
2515
- sourceEndpoint,
2516
- destinationEndpoint: endpoint,
2517
- options: DEFAULT_APS_OPTIONS,
2518
- groupId: 0,
2519
- sequence: 0, // set by stack
2520
- };
2521
- const lastHopLqi: number = 234;
2522
- // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2523
- const messageContents = Buffer.from("1803010000002003", "hex");
2524
-
2525
- mockEzspSend.mockImplementationOnce(() => {
2526
- setTimeout(async () => {
2527
- mockEzspEmitter.emit(
2528
- "incomingMessage",
2529
- EmberIncomingMessageType.UNICAST,
2530
- reverseApsFrame(apsFrame),
2531
- lastHopLqi,
2532
- networkAddress,
2533
- messageContents,
2534
- );
2535
- await flushPromises();
2536
- }, 300);
2537
-
2538
- return [SLStatus.OK, ++mockAPSSequence];
2539
- });
2540
-
2541
- const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2542
-
2543
- await vi.advanceTimersByTimeAsync(5000);
2544
- await expect(p).resolves.toStrictEqual({
2545
- clusterID: apsFrame.clusterId,
2546
- header: Zcl.Header.fromBuffer(messageContents),
2547
- address: networkAddress,
2548
- data: messageContents,
2549
- endpoint: apsFrame.destinationEndpoint,
2550
- linkquality: lastHopLqi,
2551
- groupID: apsFrame.groupId,
2552
- wasBroadcast: false,
2553
- destinationEndpoint: apsFrame.sourceEndpoint,
2554
- } as ZclPayload);
2555
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2556
- });
2557
-
2558
- it("Adapter impl: sendZclFrameToEndpoint with command response with other source endpoint", async () => {
2559
- const networkAddress: NodeId = 1234;
2560
- const endpoint: number = 1;
2561
- const sourceEndpoint = 3;
2562
- const zclFrame = Zcl.Frame.create(
2563
- Zcl.FrameType.GLOBAL,
2564
- Zcl.Direction.CLIENT_TO_SERVER,
2565
- true,
2566
- undefined,
2567
- 3,
2568
- "read",
2569
- "genBasic",
2570
- [{attrId: 0}],
2571
- {},
2572
- );
2573
- const apsFrame: EmberApsFrame = {
2574
- profileId: FIXED_ENDPOINTS[0].profileId,
2575
- clusterId: zclFrame.cluster.ID,
2576
- sourceEndpoint,
2577
- destinationEndpoint: endpoint,
2578
- options: DEFAULT_APS_OPTIONS,
2579
- groupId: 0,
2580
- sequence: 0, // set by stack
2581
- };
2582
- const lastHopLqi: number = 234;
2583
- // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2584
- const messageContents = Buffer.from("1803010000002003", "hex");
2585
-
2586
- mockEzspSend.mockImplementationOnce(() => {
2587
- setTimeout(async () => {
2588
- mockEzspEmitter.emit(
2589
- "incomingMessage",
2590
- EmberIncomingMessageType.UNICAST,
2591
- reverseApsFrame(apsFrame),
2592
- lastHopLqi,
2593
- networkAddress,
2594
- messageContents,
2595
- );
2596
- await flushPromises();
2597
- }, 300);
2598
-
2599
- return [SLStatus.OK, ++mockAPSSequence];
2600
- });
2601
-
2602
- const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2603
-
2604
- await vi.advanceTimersByTimeAsync(5000);
2605
- await expect(p).resolves.toStrictEqual({
2606
- clusterID: apsFrame.clusterId,
2607
- header: Zcl.Header.fromBuffer(messageContents),
2608
- address: networkAddress,
2609
- data: messageContents,
2610
- endpoint: apsFrame.destinationEndpoint,
2611
- linkquality: lastHopLqi,
2612
- groupID: apsFrame.groupId,
2613
- wasBroadcast: false,
2614
- destinationEndpoint: apsFrame.sourceEndpoint,
2615
- } as ZclPayload);
2616
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2617
- });
2618
-
2619
- it("Adapter impl: sendZclFrameToEndpoint with command response with no source endpoint", async () => {
2620
- const networkAddress: NodeId = 1234;
2621
- const endpoint: number = 1;
2622
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2623
- const zclFrame = Zcl.Frame.create(
2624
- Zcl.FrameType.GLOBAL,
2625
- Zcl.Direction.CLIENT_TO_SERVER,
2626
- true,
2627
- undefined,
2628
- 3,
2629
- "read",
2630
- "genBasic",
2631
- [{attrId: 0}],
2632
- {},
2633
- );
2634
- const apsFrame: EmberApsFrame = {
2635
- profileId: FIXED_ENDPOINTS[0].profileId,
2636
- clusterId: zclFrame.cluster.ID,
2637
- sourceEndpoint,
2638
- destinationEndpoint: endpoint,
2639
- options: DEFAULT_APS_OPTIONS,
2640
- groupId: 0,
2641
- sequence: 0, // set by stack
2642
- };
2643
- const lastHopLqi: number = 234;
2644
- // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2645
- const messageContents = Buffer.from("1803010000002003", "hex");
2646
-
2647
- mockEzspSend.mockImplementationOnce(() => {
2648
- setTimeout(async () => {
2649
- mockEzspEmitter.emit(
2650
- "incomingMessage",
2651
- EmberIncomingMessageType.UNICAST,
2652
- reverseApsFrame(apsFrame),
2653
- lastHopLqi,
2654
- networkAddress,
2655
- messageContents,
2656
- );
2657
- await flushPromises();
2658
- }, 300);
2659
-
2660
- return [SLStatus.OK, ++mockAPSSequence];
2661
- });
2662
-
2663
- const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false);
2664
-
2665
- await vi.advanceTimersByTimeAsync(5000);
2666
- await expect(p).resolves.toStrictEqual({
2667
- clusterID: apsFrame.clusterId,
2668
- header: Zcl.Header.fromBuffer(messageContents),
2669
- address: networkAddress,
2670
- data: messageContents,
2671
- endpoint: apsFrame.destinationEndpoint,
2672
- linkquality: lastHopLqi,
2673
- groupID: apsFrame.groupId,
2674
- wasBroadcast: false,
2675
- destinationEndpoint: apsFrame.sourceEndpoint,
2676
- } as ZclPayload);
2677
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2678
- });
2679
-
2680
- it.each([
2681
- ["NO_TX_SPACE", EzspStatus.NO_TX_SPACE],
2682
- ["NOT_CONNECTED", EzspStatus.NOT_CONNECTED],
2683
- ])("Adapter impl: recovers when sendZclFrameToEndpoint throws %s status", async (_statusName, status) => {
2684
- const networkAddress: NodeId = 1234;
2685
- const endpoint: number = 1;
2686
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2687
- const zclFrame = Zcl.Frame.create(
2688
- Zcl.FrameType.GLOBAL,
2689
- Zcl.Direction.CLIENT_TO_SERVER,
2690
- true,
2691
- undefined,
2692
- 3,
2693
- "read",
2694
- "genBasic",
2695
- [{attrId: 0}],
2696
- {},
2697
- );
2698
- const apsFrame: EmberApsFrame = {
2699
- profileId: FIXED_ENDPOINTS[0].profileId,
2700
- clusterId: zclFrame.cluster.ID,
2701
- sourceEndpoint,
2702
- destinationEndpoint: endpoint,
2703
- options: DEFAULT_APS_OPTIONS,
2704
- groupId: 0,
2705
- sequence: 0, // set by stack
2706
- };
2707
- const lastHopLqi: number = 234;
2708
- // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2709
- const messageContents = Buffer.from("1803010000002003", "hex");
2710
-
2711
- mockEzspSend.mockRejectedValueOnce(new EzspError(status)).mockImplementationOnce(() => {
2712
- setTimeout(async () => {
2713
- mockEzspEmitter.emit(
2714
- "incomingMessage",
2715
- EmberIncomingMessageType.UNICAST,
2716
- reverseApsFrame(apsFrame),
2717
- lastHopLqi,
2718
- networkAddress,
2719
- messageContents,
2720
- );
2721
- await flushPromises();
2722
- }, 300);
2723
-
2724
- return [SLStatus.OK, ++mockAPSSequence];
2725
- });
2726
-
2727
- const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2728
-
2729
- await vi.advanceTimersByTimeAsync(10000);
2730
- await expect(p).resolves.toStrictEqual({
2731
- clusterID: apsFrame.clusterId,
2732
- header: Zcl.Header.fromBuffer(messageContents),
2733
- address: networkAddress,
2734
- data: messageContents,
2735
- endpoint: apsFrame.destinationEndpoint,
2736
- linkquality: lastHopLqi,
2737
- groupID: apsFrame.groupId,
2738
- wasBroadcast: false,
2739
- destinationEndpoint: apsFrame.sourceEndpoint,
2740
- } as ZclPayload);
2741
- expect(mockEzspSend).toHaveBeenCalledTimes(2);
2742
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2743
- });
2744
-
2745
- it.each([
2746
- ["ZIGBEE_MAX_MESSAGE_LIMIT_REACHED", SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED],
2747
- ["BUSY", SLStatus.BUSY],
2748
- ["NETWORK_DOWN", SLStatus.NETWORK_DOWN],
2749
- ])("Adapter impl: recovers when sendZclFrameToEndpoint get %s status from NCP", async (_statusName, status) => {
2750
- const networkAddress: NodeId = 1234;
2751
- const endpoint: number = 1;
2752
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2753
- const zclFrame = Zcl.Frame.create(
2754
- Zcl.FrameType.GLOBAL,
2755
- Zcl.Direction.CLIENT_TO_SERVER,
2756
- true,
2757
- undefined,
2758
- 3,
2759
- "read",
2760
- "genBasic",
2761
- [{attrId: 0}],
2762
- {},
2763
- );
2764
- const apsFrame: EmberApsFrame = {
2765
- profileId: FIXED_ENDPOINTS[0].profileId,
2766
- clusterId: zclFrame.cluster.ID,
2767
- sourceEndpoint,
2768
- destinationEndpoint: endpoint,
2769
- options: DEFAULT_APS_OPTIONS,
2770
- groupId: 0,
2771
- sequence: 0, // set by stack
2772
- };
2773
- const lastHopLqi: number = 234;
2774
- // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
2775
- const messageContents = Buffer.from("1803010000002003", "hex");
2776
-
2777
- mockEzspSend.mockResolvedValueOnce([status, 0]).mockImplementationOnce(() => {
2778
- setTimeout(async () => {
2779
- mockEzspEmitter.emit(
2780
- "incomingMessage",
2781
- EmberIncomingMessageType.UNICAST,
2782
- reverseApsFrame(apsFrame),
2783
- lastHopLqi,
2784
- networkAddress,
2785
- messageContents,
2786
- );
2787
- await flushPromises();
2788
- }, 300);
2789
-
2790
- return [SLStatus.OK, ++mockAPSSequence];
2791
- });
2792
-
2793
- const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
2794
-
2795
- await vi.advanceTimersByTimeAsync(10000);
2796
- await expect(p).resolves.toStrictEqual({
2797
- clusterID: apsFrame.clusterId,
2798
- header: Zcl.Header.fromBuffer(messageContents),
2799
- address: networkAddress,
2800
- data: messageContents,
2801
- endpoint: apsFrame.destinationEndpoint,
2802
- linkquality: lastHopLqi,
2803
- groupID: apsFrame.groupId,
2804
- wasBroadcast: false,
2805
- destinationEndpoint: apsFrame.sourceEndpoint,
2806
- } as ZclPayload);
2807
- expect(mockEzspSend).toHaveBeenCalledTimes(2);
2808
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2809
- });
2810
-
2811
- it("Adapter impl: throws when sendZclFrameToEndpoint throws NO_TX_SPACE status and recovery disabled", async () => {
2812
- const networkAddress: NodeId = 1234;
2813
- const endpoint: number = 1;
2814
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2815
- const zclFrame = Zcl.Frame.create(
2816
- Zcl.FrameType.GLOBAL,
2817
- Zcl.Direction.CLIENT_TO_SERVER,
2818
- true,
2819
- undefined,
2820
- 3,
2821
- "read",
2822
- "genBasic",
2823
- [{attrId: 0}],
2824
- {},
2825
- );
2826
- const apsFrame: EmberApsFrame = {
2827
- profileId: FIXED_ENDPOINTS[0].profileId,
2828
- clusterId: zclFrame.cluster.ID,
2829
- sourceEndpoint,
2830
- destinationEndpoint: endpoint,
2831
- options: DEFAULT_APS_OPTIONS,
2832
- groupId: 0,
2833
- sequence: 0, // set by stack
2834
- };
2835
-
2836
- mockEzspSend.mockRejectedValueOnce(new EzspError(EzspStatus.NO_TX_SPACE));
2837
-
2838
- const p = defuseRejection(
2839
- adapter.sendZclFrameToEndpoint(
2840
- "0x1122334455667788",
2841
- networkAddress,
2842
- endpoint,
2843
- zclFrame,
2844
- 10000,
2845
- false,
2846
- true, // disable recovery
2847
- sourceEndpoint,
2848
- ),
2849
- );
2850
-
2851
- await vi.advanceTimersByTimeAsync(10000);
2852
- await expect(p).rejects.toThrow(
2853
- `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=${SLStatus[SLStatus.BUSY]}.`,
2854
- );
2855
- expect(mockEzspSend).toHaveBeenCalledTimes(1);
2856
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2857
- });
2858
-
2859
- it("Adapter impl: throws when sendZclFrameToEndpoint get BUSY status from NCP and recovery disabled", async () => {
2860
- const networkAddress: NodeId = 1234;
2861
- const endpoint: number = 1;
2862
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2863
- const zclFrame = Zcl.Frame.create(
2864
- Zcl.FrameType.GLOBAL,
2865
- Zcl.Direction.CLIENT_TO_SERVER,
2866
- true,
2867
- undefined,
2868
- 3,
2869
- "read",
2870
- "genBasic",
2871
- [{attrId: 0}],
2872
- {},
2873
- );
2874
- const apsFrame: EmberApsFrame = {
2875
- profileId: FIXED_ENDPOINTS[0].profileId,
2876
- clusterId: zclFrame.cluster.ID,
2877
- sourceEndpoint,
2878
- destinationEndpoint: endpoint,
2879
- options: DEFAULT_APS_OPTIONS,
2880
- groupId: 0,
2881
- sequence: 0, // set by stack
2882
- };
2883
-
2884
- mockEzspSend.mockResolvedValueOnce([SLStatus.BUSY, 0]);
2885
-
2886
- const p = defuseRejection(
2887
- adapter.sendZclFrameToEndpoint(
2888
- "0x1122334455667788",
2889
- networkAddress,
2890
- endpoint,
2891
- zclFrame,
2892
- 10000,
2893
- false,
2894
- true, // disable recovery
2895
- sourceEndpoint,
2896
- ),
2897
- );
2898
-
2899
- await vi.advanceTimersByTimeAsync(10000);
2900
- await expect(p).rejects.toThrow(
2901
- `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=${SLStatus[SLStatus.BUSY]}.`,
2902
- );
2903
- expect(mockEzspSend).toHaveBeenCalledTimes(1);
2904
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2905
- });
2906
-
2907
- it("Adapter impl: throws when sendZclFrameToEndpoint get BUSY status from NCP and exceeded max attempts", async () => {
2908
- const networkAddress: NodeId = 1234;
2909
- const endpoint: number = 1;
2910
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2911
- const zclFrame = Zcl.Frame.create(
2912
- Zcl.FrameType.GLOBAL,
2913
- Zcl.Direction.CLIENT_TO_SERVER,
2914
- true,
2915
- undefined,
2916
- 3,
2917
- "read",
2918
- "genBasic",
2919
- [{attrId: 0}],
2920
- {},
2921
- );
2922
- const apsFrame: EmberApsFrame = {
2923
- profileId: FIXED_ENDPOINTS[0].profileId,
2924
- clusterId: zclFrame.cluster.ID,
2925
- sourceEndpoint,
2926
- destinationEndpoint: endpoint,
2927
- options: DEFAULT_APS_OPTIONS,
2928
- groupId: 0,
2929
- sequence: 0, // set by stack
2930
- };
2931
-
2932
- mockEzspSend
2933
- .mockResolvedValueOnce([SLStatus.BUSY, 0])
2934
- .mockResolvedValueOnce([SLStatus.BUSY, 0])
2935
- .mockResolvedValueOnce([SLStatus.BUSY, 0]);
2936
-
2937
- const p = defuseRejection(
2938
- adapter.sendZclFrameToEndpoint(
2939
- "0x1122334455667788",
2940
- networkAddress,
2941
- endpoint,
2942
- zclFrame,
2943
- 10000,
2944
- false,
2945
- false, // disable recovery
2946
- sourceEndpoint,
2947
- ),
2948
- );
2949
-
2950
- await vi.advanceTimersByTimeAsync(10000);
2951
- await expect(p).rejects.toThrow(
2952
- `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=${SLStatus[SLStatus.BUSY]}.`,
2953
- );
2954
- expect(mockEzspSend).toHaveBeenCalledTimes(3);
2955
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
2956
- });
2957
-
2958
- it("Adapter impl: throws when sendZclFrameToEndpoint request fails", async () => {
2959
- const networkAddress: NodeId = 1234;
2960
- const endpoint: number = 1;
2961
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
2962
- const zclFrame = Zcl.Frame.create(
2963
- Zcl.FrameType.GLOBAL,
2964
- Zcl.Direction.CLIENT_TO_SERVER,
2965
- true,
2966
- undefined,
2967
- 3,
2968
- "read",
2969
- "genBasic",
2970
- [{attrId: 0}],
2971
- {},
2972
- );
2973
- const apsFrame: EmberApsFrame = {
2974
- profileId: FIXED_ENDPOINTS[0].profileId,
2975
- clusterId: zclFrame.cluster.ID,
2976
- sourceEndpoint,
2977
- destinationEndpoint: endpoint,
2978
- options: DEFAULT_APS_OPTIONS,
2979
- groupId: 0,
2980
- sequence: 0, // set by stack
2981
- };
2982
-
2983
- mockEzspSend.mockResolvedValueOnce([SLStatus.FAIL, 0]);
2984
-
2985
- const p = defuseRejection(
2986
- adapter.sendZclFrameToEndpoint(
2987
- "0x1122334455667788",
2988
- networkAddress,
2989
- endpoint,
2990
- zclFrame,
2991
- 10000,
2992
- false,
2993
- false, // disable recovery
2994
- sourceEndpoint,
2995
- ),
2996
- );
2997
-
2998
- await vi.advanceTimersByTimeAsync(10000);
2999
- await expect(p).rejects.toThrow(
3000
- `~x~> [ZCL to=0x1122334455667788:1234 apsFrame={"profileId":260,"clusterId":0,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0}] Failed to send request with status=FAIL.`,
3001
- );
3002
- expect(mockEzspSend).toHaveBeenCalledTimes(1);
3003
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3004
- });
3005
-
3006
- it("Adapter impl: sendZdo with EUI64", async () => {
3007
- const sender: NodeId = 0x6789;
3008
- const senderEUI64: Eui64 = "0x1122334455667788";
3009
- const apsFrame: EmberApsFrame = {
3010
- profileId: Zdo.ZDO_PROFILE_ID,
3011
- clusterId: Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE,
3012
- sourceEndpoint: Zdo.ZDO_ENDPOINT,
3013
- destinationEndpoint: Zdo.ZDO_ENDPOINT,
3014
- options: 0,
3015
- groupId: 0,
3016
- sequence: 0,
3017
- };
3018
-
3019
- mockEzspSendBroadcast.mockImplementationOnce(() => {
3020
- setTimeout(async () => {
3021
- mockEzspEmitter.emit(
3022
- "zdoResponse",
3023
- apsFrame,
3024
- sender,
3025
- Buffer.from([
3026
- 1,
3027
- Zdo.Status.SUCCESS,
3028
- 0x88,
3029
- 0x77,
3030
- 0x66,
3031
- 0x55,
3032
- 0x44,
3033
- 0x33,
3034
- 0x22,
3035
- 0x11,
3036
- 0x89, // nwkAddress
3037
- 0x67, // nwkAddress
3038
- ]),
3039
- );
3040
- await flushPromises();
3041
- }, 300);
3042
-
3043
- return [SLStatus.OK, ++mockAPSSequence];
3044
- });
3045
-
3046
- const zdoPayload = Zdo.Buffalo.buildRequest(false, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, senderEUI64, false, 0);
3047
- const p = adapter.sendZdo(
3048
- senderEUI64,
3049
- ZSpec.NULL_NODE_ID /* same as broadcast SLEEPY */,
3050
- Zdo.ClusterId.NETWORK_ADDRESS_REQUEST,
3051
- zdoPayload,
3052
- false,
3053
- );
3054
-
3055
- await vi.advanceTimersByTimeAsync(1000);
3056
- await expect(p).resolves.toStrictEqual([
3057
- Zdo.Status.SUCCESS,
3058
- {
3059
- eui64: senderEUI64,
3060
- nwkAddress: sender,
3061
- startIndex: 0,
3062
- assocDevList: [],
3063
- } as ZdoTypes.NetworkAddressResponse,
3064
- ]);
3065
- });
3066
-
3067
- it("Adapter impl: sendZclFrameToEndpoint with default response", async () => {
3068
- const networkAddress: NodeId = 1234;
3069
- const endpoint: number = 3;
3070
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3071
- const zclFrame = Zcl.Frame.create(
3072
- Zcl.FrameType.GLOBAL,
3073
- Zcl.Direction.CLIENT_TO_SERVER,
3074
- false,
3075
- undefined,
3076
- 3,
3077
- "read",
3078
- "genBasic",
3079
- [{attrId: 0}],
3080
- {},
3081
- );
3082
- const apsFrame: EmberApsFrame = {
3083
- profileId: FIXED_ENDPOINTS[0].profileId,
3084
- clusterId: zclFrame.cluster.ID,
3085
- sourceEndpoint,
3086
- destinationEndpoint: endpoint,
3087
- options: DEFAULT_APS_OPTIONS,
3088
- groupId: 0,
3089
- sequence: 0, // set by stack
3090
- };
3091
- const lastHopLqi: number = 234;
3092
- // defaultRsp with cmdId=0, status=0
3093
- const messageContents = Buffer.from("18030b0000", "hex");
3094
-
3095
- mockEzspSend.mockImplementationOnce(() => {
3096
- setTimeout(async () => {
3097
- mockEzspEmitter.emit(
3098
- "incomingMessage",
3099
- EmberIncomingMessageType.UNICAST,
3100
- reverseApsFrame(apsFrame),
3101
- lastHopLqi,
3102
- networkAddress,
3103
- messageContents,
3104
- );
3105
- await flushPromises();
3106
- }, 300);
3107
-
3108
- return [SLStatus.OK, ++mockAPSSequence];
3109
- });
3110
-
3111
- const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, true, false, sourceEndpoint);
3112
-
3113
- await vi.advanceTimersByTimeAsync(5000);
3114
- await expect(p).resolves.toStrictEqual({
3115
- clusterID: apsFrame.clusterId,
3116
- header: Zcl.Header.fromBuffer(messageContents),
3117
- address: networkAddress,
3118
- data: messageContents,
3119
- endpoint: apsFrame.destinationEndpoint,
3120
- linkquality: lastHopLqi,
3121
- groupID: apsFrame.groupId,
3122
- wasBroadcast: false,
3123
- destinationEndpoint: apsFrame.sourceEndpoint,
3124
- } as ZclPayload);
3125
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3126
- });
3127
-
3128
- it("Adapter impl: sendZclFrameToEndpoint without response", async () => {
3129
- const networkAddress: NodeId = 1234;
3130
- const endpoint: number = 3;
3131
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3132
- const zclFrame = Zcl.Frame.create(
3133
- Zcl.FrameType.GLOBAL,
3134
- Zcl.Direction.CLIENT_TO_SERVER,
3135
- true,
3136
- undefined,
3137
- 3,
3138
- "read",
3139
- "genBasic",
3140
- [{attrId: 0}],
3141
- {},
3142
- );
3143
-
3144
- const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, true, false, sourceEndpoint);
3145
-
3146
- await vi.advanceTimersByTimeAsync(5000);
3147
- await expect(p).resolves.toStrictEqual(undefined);
3148
-
3149
- const apsFrame: EmberApsFrame = {
3150
- profileId: FIXED_ENDPOINTS[0].profileId,
3151
- clusterId: zclFrame.cluster.ID,
3152
- sourceEndpoint,
3153
- destinationEndpoint: endpoint,
3154
- options: DEFAULT_APS_OPTIONS & ~EmberApsOption.RETRY,
3155
- groupId: 0,
3156
- sequence: 0, // set by stack
3157
- };
3158
-
3159
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3160
- });
3161
-
3162
- it("Adapter impl: sendZclFrameToGroup with source endpoint", async () => {
3163
- const groupId: number = 32;
3164
- const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3165
- const p = adapter.sendZclFrameToGroup(groupId, zclFrame, 2);
3166
-
3167
- await vi.advanceTimersByTimeAsync(5000);
3168
- await expect(p).resolves.toStrictEqual(undefined);
3169
-
3170
- const apsFrame: EmberApsFrame = {
3171
- profileId: FIXED_ENDPOINTS[0].profileId,
3172
- clusterId: zclFrame.cluster.ID,
3173
- sourceEndpoint: 2,
3174
- destinationEndpoint: 0xff,
3175
- options: DEFAULT_APS_OPTIONS,
3176
- groupId,
3177
- sequence: 0, // set by stack
3178
- };
3179
-
3180
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.MULTICAST, groupId, apsFrame, zclFrame.toBuffer(), 0, 0);
3181
- });
3182
-
3183
- it("Adapter impl: sendZclFrameToGroup with default source endpoint", async () => {
3184
- const groupId: number = 32;
3185
- const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3186
- const p = adapter.sendZclFrameToGroup(groupId, zclFrame);
3187
-
3188
- await vi.advanceTimersByTimeAsync(5000);
3189
- await expect(p).resolves.toStrictEqual(undefined);
3190
-
3191
- const apsFrame: EmberApsFrame = {
3192
- profileId: FIXED_ENDPOINTS[0].profileId,
3193
- clusterId: zclFrame.cluster.ID,
3194
- sourceEndpoint: FIXED_ENDPOINTS[0].endpoint,
3195
- destinationEndpoint: 0xff,
3196
- options: DEFAULT_APS_OPTIONS,
3197
- groupId,
3198
- sequence: 0, // set by stack
3199
- };
3200
-
3201
- expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.MULTICAST, groupId, apsFrame, zclFrame.toBuffer(), 0, 0);
3202
- });
3203
-
3204
- it("Adapter impl: throws when sendZclFrameToGroup fails request", async () => {
3205
- mockEzspSend.mockResolvedValueOnce([SLStatus.FAIL, 0]);
3206
-
3207
- const groupId: number = 32;
3208
- const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3209
- const p = defuseRejection(adapter.sendZclFrameToGroup(groupId, zclFrame, 1));
3210
-
3211
- await vi.advanceTimersByTimeAsync(5000);
3212
- await expect(p).rejects.toThrow("~x~> [ZCL GROUP groupId=32] Failed to send with status=FAIL.");
3213
- expect(mockEzspSend).toHaveBeenCalledTimes(1);
3214
- });
3215
-
3216
- it("Adapter impl: sendZclFrameToAll with fixed endpoint", async () => {
3217
- const endpoint: number = 32;
3218
- const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3219
- const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3220
- const p = adapter.sendZclFrameToAll(endpoint, zclFrame, sourceEndpoint, ZSpec.BroadcastAddress.DEFAULT);
3221
-
3222
- await vi.advanceTimersByTimeAsync(5000);
3223
- await expect(p).resolves.toStrictEqual(undefined);
3224
-
3225
- const apsFrame: EmberApsFrame = {
3226
- profileId: FIXED_ENDPOINTS[0].profileId,
3227
- clusterId: zclFrame.cluster.ID,
3228
- sourceEndpoint,
3229
- destinationEndpoint: endpoint,
3230
- options: DEFAULT_APS_OPTIONS,
3231
- groupId: ZSpec.BroadcastAddress.DEFAULT,
3232
- sequence: 0, // set by stack
3233
- };
3234
-
3235
- expect(mockEzspSend).toHaveBeenCalledWith(
3236
- EmberOutgoingMessageType.BROADCAST,
3237
- ZSpec.BroadcastAddress.DEFAULT,
3238
- apsFrame,
3239
- zclFrame.toBuffer(),
3240
- 0,
3241
- 0,
3242
- );
3243
- });
3244
-
3245
- it("Adapter impl: sendZclFrameToAll with other endpoint", async () => {
3246
- const endpoint: number = 32;
3247
- const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3248
- const sourceEndpoint = 3;
3249
- const p = adapter.sendZclFrameToAll(endpoint, zclFrame, sourceEndpoint, ZSpec.BroadcastAddress.DEFAULT);
3250
-
3251
- await vi.advanceTimersByTimeAsync(5000);
3252
- await expect(p).resolves.toStrictEqual(undefined);
3253
-
3254
- const apsFrame: EmberApsFrame = {
3255
- profileId: FIXED_ENDPOINTS[0].profileId,
3256
- clusterId: zclFrame.cluster.ID,
3257
- sourceEndpoint,
3258
- destinationEndpoint: endpoint,
3259
- options: DEFAULT_APS_OPTIONS,
3260
- groupId: ZSpec.BroadcastAddress.DEFAULT,
3261
- sequence: 0, // set by stack
3262
- };
3263
-
3264
- expect(mockEzspSend).toHaveBeenCalledWith(
3265
- EmberOutgoingMessageType.BROADCAST,
3266
- ZSpec.BroadcastAddress.DEFAULT,
3267
- apsFrame,
3268
- zclFrame.toBuffer(),
3269
- 0,
3270
- 0,
3271
- );
3272
- });
3273
-
3274
- it("Adapter impl: throws when sendZclFrameToAll fails request", async () => {
3275
- mockEzspSend.mockResolvedValueOnce([SLStatus.FAIL, 0]);
3276
-
3277
- const endpoint: number = 32;
3278
- const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
3279
- const p = defuseRejection(adapter.sendZclFrameToAll(endpoint, zclFrame, 1, ZSpec.BroadcastAddress.DEFAULT));
3280
-
3281
- await vi.advanceTimersByTimeAsync(5000);
3282
- await expect(p).rejects.toThrow("~x~> [ZCL BROADCAST destination=65532] Failed to send with status=FAIL.");
3283
- expect(mockEzspSend).toHaveBeenCalledTimes(1);
3284
- });
3285
-
3286
- it("Adapter impl: setChannelInterPAN", async () => {
3287
- await expect(adapter.setChannelInterPAN(15)).resolves.toStrictEqual(undefined);
3288
- expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(15);
3289
- });
3290
-
3291
- it("Adapter impl: throws when setChannelInterPAN fails request", async () => {
3292
- mockEzspSetLogicalAndRadioChannel.mockResolvedValueOnce(SLStatus.FAIL);
3293
-
3294
- await expect(adapter.setChannelInterPAN(15)).rejects.toThrow(`Failed to set InterPAN channel to '15' with status=FAIL.`);
3295
- expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(15);
3296
- });
3297
-
3298
- it("Adapter impl: sendZclFrameInterPANToIeeeAddr", async () => {
3299
- const ieee: Eui64 = "0x1122334455667788";
3300
- const zclFrame = Zcl.Frame.create(
3301
- Zcl.FrameType.GLOBAL,
3302
- Zcl.Direction.CLIENT_TO_SERVER,
3303
- false,
3304
- undefined,
3305
- 3,
3306
- "read",
3307
- "genBasic",
3308
- [{attrId: 0}],
3309
- {},
3310
- );
3311
-
3312
- await expect(adapter.sendZclFrameInterPANToIeeeAddr(zclFrame, ieee)).resolves.toStrictEqual(undefined);
3313
- expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3314
- expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3315
- });
3316
-
3317
- it("Adapter impl: throws when sendZclFrameInterPANToIeeeAddr request fails", async () => {
3318
- mockEzspSendRawMessage.mockResolvedValueOnce(SLStatus.BUSY);
3319
-
3320
- const ieee: Eui64 = "0x1122334455667788";
3321
- const zclFrame = Zcl.Frame.create(
3322
- Zcl.FrameType.GLOBAL,
3323
- Zcl.Direction.CLIENT_TO_SERVER,
3324
- false,
3325
- undefined,
3326
- 3,
3327
- "read",
3328
- "genBasic",
3329
- [{attrId: 0}],
3330
- {},
3331
- );
3332
-
3333
- await expect(adapter.sendZclFrameInterPANToIeeeAddr(zclFrame, ieee)).rejects.toThrow(
3334
- `~x~> [ZCL TOUCHLINK to=${ieee}] Failed to send with status=${SLStatus[SLStatus.BUSY]}.`,
3335
- );
3336
- expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3337
- expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3338
- });
3339
-
3340
- it("Adapter impl: sendZclFrameInterPANBroadcast", async () => {
3341
- const zclFrame = Zcl.Frame.create(
3342
- Zcl.FrameType.SPECIFIC,
3343
- Zcl.Direction.CLIENT_TO_SERVER,
3344
- true,
3345
- undefined,
3346
- 0,
3347
- "scanRequest",
3348
- Zcl.Clusters.touchlink.ID,
3349
- {transactionID: 1, zigbeeInformation: 4, touchlinkInformation: 18},
3350
- {},
3351
- );
3352
- const sourcePanId: PanId = 0x1234;
3353
- const sourceAddress: Eui64 = "0x1122334455aabbcc";
3354
- const groupId: number = ZSpec.BroadcastAddress.SLEEPY;
3355
- const lastHopLqi = 252;
3356
- // Received Zigbee message from '0x', type 'readResponse', cluster 'genBasic', data '{"zclVersion":3}' from endpoint 1 with groupID 0
3357
- const messageContents = Buffer.from("1800010000000100000000000000000088776655443322110154466341200", "hex");
3358
-
3359
- mockEzspSendRawMessage.mockImplementationOnce(() => {
3360
- setTimeout(async () => {
3361
- mockEzspEmitter.emit("touchlinkMessage", sourcePanId, sourceAddress, groupId, lastHopLqi, messageContents);
3362
- await flushPromises();
3363
- }, 300);
3364
-
3365
- return SLStatus.OK;
3366
- });
3367
-
3368
- const p = adapter.sendZclFrameInterPANBroadcast(zclFrame, 10000);
3369
-
3370
- await vi.advanceTimersByTimeAsync(5000);
3371
-
3372
- const payload: ZclPayload = {
3373
- clusterID: Zcl.Clusters.touchlink.ID,
3374
- header: Zcl.Header.fromBuffer(messageContents),
3375
- address: sourceAddress,
3376
- data: messageContents,
3377
- endpoint: FIXED_ENDPOINTS[0].endpoint,
3378
- linkquality: lastHopLqi,
3379
- groupID: groupId,
3380
- wasBroadcast: true,
3381
- destinationEndpoint: FIXED_ENDPOINTS[0].endpoint,
3382
- };
3383
-
3384
- await expect(p).resolves.toStrictEqual(payload);
3385
- expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3386
- expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3387
- });
3388
-
3389
- it("Adapter impl: throws when sendZclFrameInterPANBroadcast command has no response", async () => {
3390
- const commandName = "readRsp";
3391
- const zclFrame = Zcl.Frame.create(
3392
- Zcl.FrameType.GLOBAL,
3393
- Zcl.Direction.CLIENT_TO_SERVER,
3394
- false,
3395
- undefined,
3396
- 3,
3397
- commandName,
3398
- "genBasic",
3399
- [{attrId: 0}],
3400
- {},
3401
- );
3402
-
3403
- await expect(adapter.sendZclFrameInterPANBroadcast(zclFrame, 10000)).rejects.toThrow(
3404
- `Command '${commandName}' has no response, cannot wait for response.`,
3405
- );
3406
- expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(0);
3407
- });
3408
-
3409
- it("Adapter impl: throws when sendZclFrameInterPANBroadcast request fails", async () => {
3410
- mockEzspSendRawMessage.mockResolvedValueOnce(SLStatus.BUSY);
3411
-
3412
- const zclFrame = Zcl.Frame.create(
3413
- Zcl.FrameType.GLOBAL,
3414
- Zcl.Direction.CLIENT_TO_SERVER,
3415
- false,
3416
- undefined,
3417
- 3,
3418
- "read",
3419
- "genBasic",
3420
- [{attrId: 0}],
3421
- {},
3422
- );
3423
-
3424
- await expect(adapter.sendZclFrameInterPANBroadcast(zclFrame, 10000)).rejects.toThrow(
3425
- `~x~> [ZCL TOUCHLINK BROADCAST] Failed to send with status=${SLStatus[SLStatus.BUSY]}.`,
3426
- );
3427
- expect(mockEzspSendRawMessage).toHaveBeenCalledTimes(1);
3428
- expect(mockEzspSendRawMessage).toHaveBeenCalledWith(expect.any(Buffer), 1, true);
3429
- });
3430
-
3431
- it("Adapter impl: restoreChannelInterPAN", async () => {
3432
- const p = adapter.restoreChannelInterPAN();
3433
-
3434
- await vi.advanceTimersByTimeAsync(10000);
3435
- await expect(p).resolves.toStrictEqual(undefined);
3436
- expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(DEFAULT_NETWORK_OPTIONS.channelList[0]);
3437
- });
3438
-
3439
- it("Adapter impl: throws when restoreChannelInterPAN fails request", async () => {
3440
- mockEzspSetLogicalAndRadioChannel.mockResolvedValueOnce(SLStatus.FAIL);
3441
-
3442
- const p = defuseRejection(adapter.restoreChannelInterPAN());
3443
-
3444
- await vi.advanceTimersByTimeAsync(10000);
3445
- await expect(p).rejects.toThrow(`Failed to restore InterPAN channel to '${DEFAULT_NETWORK_OPTIONS.channelList[0]}' with status=FAIL.`);
3446
- expect(mockEzspSetLogicalAndRadioChannel).toHaveBeenCalledWith(DEFAULT_NETWORK_OPTIONS.channelList[0]);
3447
- });
3448
- });
3449
- });