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