@willieee802/zigbee-herdsman 0.15.0 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/adapter.d.ts +61 -61
- package/dist/adapter/adapter.d.ts.map +1 -1
- package/dist/adapter/adapter.js +146 -100
- package/dist/adapter/adapter.js.map +1 -1
- package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +68 -68
- package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -1
- package/dist/adapter/deconz/adapter/deconzAdapter.js +1060 -1063
- package/dist/adapter/deconz/adapter/deconzAdapter.js.map +1 -1
- package/dist/adapter/deconz/adapter/index.d.ts +2 -2
- package/dist/adapter/deconz/adapter/index.js +10 -10
- package/dist/adapter/deconz/driver/constants.d.ts +104 -104
- package/dist/adapter/deconz/driver/constants.js +55 -55
- package/dist/adapter/deconz/driver/driver.d.ts +81 -81
- package/dist/adapter/deconz/driver/driver.js +732 -732
- package/dist/adapter/deconz/driver/frame.d.ts +6 -6
- package/dist/adapter/deconz/driver/frame.js +13 -13
- package/dist/adapter/deconz/driver/frameParser.d.ts +2 -2
- package/dist/adapter/deconz/driver/frameParser.js +443 -443
- package/dist/adapter/deconz/driver/parser.d.ts +12 -12
- package/dist/adapter/deconz/driver/parser.js +61 -61
- package/dist/adapter/deconz/driver/writer.d.ts +8 -8
- package/dist/adapter/deconz/driver/writer.js +44 -44
- package/dist/adapter/events.d.ts +47 -47
- package/dist/adapter/events.js +14 -14
- package/dist/adapter/ezsp/adapter/backup.d.ts +9 -9
- package/dist/adapter/ezsp/adapter/backup.js +53 -53
- package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +59 -59
- package/dist/adapter/ezsp/adapter/ezspAdapter.js +585 -585
- package/dist/adapter/ezsp/adapter/index.d.ts +2 -2
- package/dist/adapter/ezsp/adapter/index.js +10 -10
- package/dist/adapter/ezsp/driver/commands.d.ts +36 -34
- package/dist/adapter/ezsp/driver/commands.d.ts.map +1 -1
- package/dist/adapter/ezsp/driver/commands.js +2359 -2357
- package/dist/adapter/ezsp/driver/commands.js.map +1 -1
- package/dist/adapter/ezsp/driver/consts.d.ts +10 -10
- package/dist/adapter/ezsp/driver/consts.js +13 -13
- package/dist/adapter/ezsp/driver/driver.d.ts +103 -103
- package/dist/adapter/ezsp/driver/driver.js +639 -639
- package/dist/adapter/ezsp/driver/driver.js.map +1 -1
- package/dist/adapter/ezsp/driver/ezsp.d.ts +96 -96
- package/dist/adapter/ezsp/driver/ezsp.d.ts.map +1 -1
- package/dist/adapter/ezsp/driver/ezsp.js +586 -577
- package/dist/adapter/ezsp/driver/ezsp.js.map +1 -1
- package/dist/adapter/ezsp/driver/index.d.ts +3 -3
- package/dist/adapter/ezsp/driver/index.js +8 -8
- package/dist/adapter/ezsp/driver/multicast.d.ts +12 -12
- package/dist/adapter/ezsp/driver/multicast.js +74 -74
- package/dist/adapter/ezsp/driver/parser.d.ts +12 -12
- package/dist/adapter/ezsp/driver/parser.js +111 -111
- package/dist/adapter/ezsp/driver/types/basic.d.ts +62 -62
- package/dist/adapter/ezsp/driver/types/basic.js +208 -208
- package/dist/adapter/ezsp/driver/types/basic.js.map +1 -1
- package/dist/adapter/ezsp/driver/types/index.d.ts +9 -9
- package/dist/adapter/ezsp/driver/types/index.js +133 -133
- package/dist/adapter/ezsp/driver/types/named.d.ts +697 -697
- package/dist/adapter/ezsp/driver/types/named.js +1726 -1726
- package/dist/adapter/ezsp/driver/types/named.js.map +1 -1
- package/dist/adapter/ezsp/driver/types/struct.d.ts +251 -251
- package/dist/adapter/ezsp/driver/types/struct.js +708 -708
- package/dist/adapter/ezsp/driver/types/struct.js.map +1 -1
- package/dist/adapter/ezsp/driver/uart.d.ts +44 -44
- package/dist/adapter/ezsp/driver/uart.d.ts.map +1 -1
- package/dist/adapter/ezsp/driver/uart.js +368 -366
- package/dist/adapter/ezsp/driver/uart.js.map +1 -1
- package/dist/adapter/ezsp/driver/utils/crc16ccitt.d.ts +2 -2
- package/dist/adapter/ezsp/driver/utils/crc16ccitt.js +55 -55
- package/dist/adapter/ezsp/driver/utils/index.d.ts +18 -18
- package/dist/adapter/ezsp/driver/utils/index.js +67 -67
- package/dist/adapter/ezsp/driver/writer.d.ts +13 -13
- package/dist/adapter/ezsp/driver/writer.js +88 -88
- package/dist/adapter/index.d.ts +4 -4
- package/dist/adapter/index.js +35 -35
- package/dist/adapter/serialPort.d.ts +8 -8
- package/dist/adapter/serialPort.js +22 -22
- package/dist/adapter/serialPort.js.map +1 -1
- package/dist/adapter/serialPortUtils.d.ts +12 -12
- package/dist/adapter/serialPortUtils.js +18 -18
- package/dist/adapter/socketPortUtils.d.ts +10 -10
- package/dist/adapter/socketPortUtils.js +16 -16
- package/dist/adapter/tstype.d.ts +85 -85
- package/dist/adapter/tstype.js +2 -2
- package/dist/adapter/z-stack/adapter/adapter-backup.d.ts +62 -62
- package/dist/adapter/z-stack/adapter/adapter-backup.js +441 -441
- package/dist/adapter/z-stack/adapter/adapter-nv-memory.d.ts +150 -150
- package/dist/adapter/z-stack/adapter/adapter-nv-memory.js +258 -258
- package/dist/adapter/z-stack/adapter/endpoints.d.ts +11 -11
- package/dist/adapter/z-stack/adapter/endpoints.js +73 -73
- package/dist/adapter/z-stack/adapter/index.d.ts +2 -2
- package/dist/adapter/z-stack/adapter/index.js +8 -8
- package/dist/adapter/z-stack/adapter/manager.d.ts +86 -86
- package/dist/adapter/z-stack/adapter/manager.js +476 -476
- package/dist/adapter/z-stack/adapter/tstype.d.ts +6 -6
- package/dist/adapter/z-stack/adapter/tstype.js +10 -10
- package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +81 -81
- package/dist/adapter/z-stack/adapter/zStackAdapter.js +868 -868
- package/dist/adapter/z-stack/constants/af.d.ts +23 -23
- package/dist/adapter/z-stack/constants/af.js +27 -27
- package/dist/adapter/z-stack/constants/common.d.ts +278 -278
- package/dist/adapter/z-stack/constants/common.js +289 -289
- package/dist/adapter/z-stack/constants/dbg.d.ts +22 -22
- package/dist/adapter/z-stack/constants/dbg.js +24 -24
- package/dist/adapter/z-stack/constants/index.d.ts +10 -10
- package/dist/adapter/z-stack/constants/index.js +47 -47
- package/dist/adapter/z-stack/constants/mac.d.ts +127 -127
- package/dist/adapter/z-stack/constants/mac.js +129 -129
- package/dist/adapter/z-stack/constants/sapi.d.ts +24 -24
- package/dist/adapter/z-stack/constants/sapi.js +26 -26
- package/dist/adapter/z-stack/constants/sys.d.ts +71 -71
- package/dist/adapter/z-stack/constants/sys.js +73 -73
- package/dist/adapter/z-stack/constants/util.d.ts +81 -81
- package/dist/adapter/z-stack/constants/util.js +83 -83
- package/dist/adapter/z-stack/constants/utils.d.ts +4 -4
- package/dist/adapter/z-stack/constants/utils.js +14 -14
- package/dist/adapter/z-stack/constants/zdo.d.ts +102 -102
- package/dist/adapter/z-stack/constants/zdo.js +104 -104
- package/dist/adapter/z-stack/models/index.d.ts +1 -1
- package/dist/adapter/z-stack/models/index.js +17 -17
- package/dist/adapter/z-stack/models/startup-options.d.ts +12 -12
- package/dist/adapter/z-stack/models/startup-options.js +2 -2
- package/dist/adapter/z-stack/structs/entries/address-manager-entry.d.ts +23 -23
- package/dist/adapter/z-stack/structs/entries/address-manager-entry.js +45 -45
- package/dist/adapter/z-stack/structs/entries/address-manager-table.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/address-manager-table.js +22 -22
- package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/aps-link-key-data-entry.js +21 -21
- package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/aps-link-key-data-table.js +23 -23
- package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-entry.js +24 -24
- package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/aps-tc-link-key-table.js +23 -23
- package/dist/adapter/z-stack/structs/entries/channel-list.d.ts +8 -8
- package/dist/adapter/z-stack/structs/entries/channel-list.js +15 -15
- package/dist/adapter/z-stack/structs/entries/has-configured.d.ts +8 -8
- package/dist/adapter/z-stack/structs/entries/has-configured.js +16 -16
- package/dist/adapter/z-stack/structs/entries/index.d.ts +16 -16
- package/dist/adapter/z-stack/structs/entries/index.js +32 -32
- package/dist/adapter/z-stack/structs/entries/nib.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/nib.js +68 -68
- package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/nwk-key-descriptor.js +18 -18
- package/dist/adapter/z-stack/structs/entries/nwk-key.d.ts +8 -8
- package/dist/adapter/z-stack/structs/entries/nwk-key.js +15 -15
- package/dist/adapter/z-stack/structs/entries/nwk-pan-id.d.ts +8 -8
- package/dist/adapter/z-stack/structs/entries/nwk-pan-id.js +15 -15
- package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.d.ts +13 -13
- package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-entry.js +23 -23
- package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/nwk-sec-material-descriptor-table.js +22 -22
- package/dist/adapter/z-stack/structs/entries/security-manager-entry.d.ts +20 -20
- package/dist/adapter/z-stack/structs/entries/security-manager-entry.js +36 -36
- package/dist/adapter/z-stack/structs/entries/security-manager-table.d.ts +10 -10
- package/dist/adapter/z-stack/structs/entries/security-manager-table.js +24 -24
- package/dist/adapter/z-stack/structs/index.d.ts +4 -4
- package/dist/adapter/z-stack/structs/index.js +20 -20
- package/dist/adapter/z-stack/structs/serializable-memory-object.d.ts +13 -13
- package/dist/adapter/z-stack/structs/serializable-memory-object.js +2 -2
- package/dist/adapter/z-stack/structs/struct.d.ts +99 -99
- package/dist/adapter/z-stack/structs/struct.js +295 -295
- package/dist/adapter/z-stack/structs/table.d.ts +94 -94
- package/dist/adapter/z-stack/structs/table.js +161 -161
- package/dist/adapter/z-stack/unpi/constants.d.ts +28 -28
- package/dist/adapter/z-stack/unpi/constants.js +41 -41
- package/dist/adapter/z-stack/unpi/frame.d.ts +16 -16
- package/dist/adapter/z-stack/unpi/frame.js +48 -48
- package/dist/adapter/z-stack/unpi/index.d.ts +5 -5
- package/dist/adapter/z-stack/unpi/index.js +37 -37
- package/dist/adapter/z-stack/unpi/parser.d.ts +10 -10
- package/dist/adapter/z-stack/unpi/parser.js +74 -74
- package/dist/adapter/z-stack/unpi/writer.d.ts +10 -10
- package/dist/adapter/z-stack/unpi/writer.js +44 -44
- package/dist/adapter/z-stack/utils/channel-list.d.ts +20 -20
- package/dist/adapter/z-stack/utils/channel-list.js +40 -40
- package/dist/adapter/z-stack/utils/index.d.ts +2 -2
- package/dist/adapter/z-stack/utils/index.js +18 -18
- package/dist/adapter/z-stack/utils/network-options.d.ts +8 -8
- package/dist/adapter/z-stack/utils/network-options.js +22 -22
- package/dist/adapter/z-stack/znp/buffaloZnp.d.ts +11 -11
- package/dist/adapter/z-stack/znp/buffaloZnp.js +113 -113
- package/dist/adapter/z-stack/znp/definition.d.ts +5 -5
- package/dist/adapter/z-stack/znp/definition.js +3050 -3050
- package/dist/adapter/z-stack/znp/index.d.ts +3 -3
- package/dist/adapter/z-stack/znp/index.js +10 -10
- package/dist/adapter/z-stack/znp/parameterType.d.ts +22 -22
- package/dist/adapter/z-stack/znp/parameterType.js +25 -25
- package/dist/adapter/z-stack/znp/tstype.d.ts +21 -21
- package/dist/adapter/z-stack/znp/tstype.js +2 -2
- package/dist/adapter/z-stack/znp/znp.d.ts +43 -43
- package/dist/adapter/z-stack/znp/znp.js +325 -325
- package/dist/adapter/z-stack/znp/zpiObject.d.ts +19 -19
- package/dist/adapter/z-stack/znp/zpiObject.js +96 -96
- package/dist/adapter/zigate/adapter/index.d.ts +2 -2
- package/dist/adapter/zigate/adapter/index.js +10 -10
- package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +69 -69
- package/dist/adapter/zigate/adapter/zigateAdapter.js +678 -678
- package/dist/adapter/zigate/debug.d.ts +7 -7
- package/dist/adapter/zigate/debug.js +22 -22
- package/dist/adapter/zigate/driver/buffaloZiGate.d.ts +18 -18
- package/dist/adapter/zigate/driver/buffaloZiGate.js +139 -139
- package/dist/adapter/zigate/driver/commandType.d.ts +41 -41
- package/dist/adapter/zigate/driver/commandType.js +385 -385
- package/dist/adapter/zigate/driver/constants.d.ts +276 -276
- package/dist/adapter/zigate/driver/constants.js +371 -371
- package/dist/adapter/zigate/driver/frame.d.ts +26 -26
- package/dist/adapter/zigate/driver/frame.js +172 -172
- package/dist/adapter/zigate/driver/frame.js.map +1 -1
- package/dist/adapter/zigate/driver/messageType.d.ts +11 -11
- package/dist/adapter/zigate/driver/messageType.js +278 -278
- package/dist/adapter/zigate/driver/parameterType.d.ts +20 -20
- package/dist/adapter/zigate/driver/parameterType.js +23 -23
- package/dist/adapter/zigate/driver/ziGateObject.d.ts +23 -23
- package/dist/adapter/zigate/driver/ziGateObject.js +106 -106
- package/dist/adapter/zigate/driver/zigate.d.ts +49 -49
- package/dist/adapter/zigate/driver/zigate.js +303 -303
- package/dist/buffalo/buffalo.d.ts +50 -48
- package/dist/buffalo/buffalo.d.ts.map +1 -1
- package/dist/buffalo/buffalo.js +322 -307
- package/dist/buffalo/buffalo.js.map +1 -1
- package/dist/buffalo/index.d.ts +3 -3
- package/dist/buffalo/index.js +33 -33
- package/dist/buffalo/tstype.d.ts +8 -8
- package/dist/buffalo/tstype.js +2 -2
- package/dist/controller/controller.d.ts +110 -110
- package/dist/controller/controller.js +607 -607
- package/dist/controller/database.d.ts +18 -18
- package/dist/controller/database.js +93 -93
- package/dist/controller/events.d.ts +55 -55
- package/dist/controller/events.d.ts.map +1 -1
- package/dist/controller/events.js +101 -99
- package/dist/controller/events.js.map +1 -1
- package/dist/controller/greenPower.d.ts +12 -12
- package/dist/controller/greenPower.js +220 -220
- package/dist/controller/helpers/index.d.ts +2 -2
- package/dist/controller/helpers/index.js +28 -28
- package/dist/controller/helpers/request.d.ts +23 -0
- package/dist/controller/helpers/request.d.ts.map +1 -0
- package/dist/controller/helpers/request.js +72 -0
- package/dist/controller/helpers/request.js.map +1 -0
- package/dist/controller/helpers/zclFrameConverter.d.ts +7 -7
- package/dist/controller/helpers/zclFrameConverter.js +31 -31
- package/dist/controller/helpers/zclTransactionSequenceNumber.d.ts +5 -5
- package/dist/controller/helpers/zclTransactionSequenceNumber.js +13 -13
- package/dist/controller/index.d.ts +5 -5
- package/dist/controller/index.js +8 -8
- package/dist/controller/logger-stub.d.ts +6 -6
- package/dist/controller/logger-stub.js +2 -2
- package/dist/controller/model/device.d.ts +132 -127
- package/dist/controller/model/device.d.ts.map +1 -1
- package/dist/controller/model/device.js +708 -681
- package/dist/controller/model/device.js.map +1 -1
- package/dist/controller/model/endpoint.d.ts +131 -128
- package/dist/controller/model/endpoint.d.ts.map +1 -1
- package/dist/controller/model/endpoint.js +817 -665
- package/dist/controller/model/endpoint.js.map +1 -1
- package/dist/controller/model/entity.d.ts +14 -14
- package/dist/controller/model/entity.js +26 -26
- package/dist/controller/model/group.d.ts +38 -39
- package/dist/controller/model/group.d.ts.map +1 -1
- package/dist/controller/model/group.js +221 -221
- package/dist/controller/model/group.js.map +1 -1
- package/dist/controller/model/index.d.ts +5 -5
- package/dist/controller/model/index.js +14 -14
- package/dist/controller/touchlink.d.ts +19 -19
- package/dist/controller/touchlink.js +157 -157
- package/dist/controller/tstype.d.ts +21 -20
- package/dist/controller/tstype.d.ts.map +1 -1
- package/dist/controller/tstype.js +9 -9
- package/dist/controller/tstype.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +33 -33
- package/dist/models/backup-storage-legacy.d.ts +26 -26
- package/dist/models/backup-storage-legacy.js +2 -2
- package/dist/models/backup-storage-unified.d.ts +49 -49
- package/dist/models/backup-storage-unified.js +2 -2
- package/dist/models/backup.d.ts +37 -37
- package/dist/models/backup.js +2 -2
- package/dist/models/index.d.ts +4 -4
- package/dist/models/index.js +20 -20
- package/dist/models/network-options.d.ts +12 -12
- package/dist/models/network-options.js +2 -2
- package/dist/utils/assertString.d.ts +2 -2
- package/dist/utils/assertString.js +8 -8
- package/dist/utils/backup.d.ts +20 -20
- package/dist/utils/backup.js +187 -187
- package/dist/utils/equalsPartial.d.ts +2 -2
- package/dist/utils/equalsPartial.js +11 -11
- package/dist/utils/index.d.ts +9 -9
- package/dist/utils/index.js +45 -45
- package/dist/utils/isNumberArray.d.ts +2 -2
- package/dist/utils/isNumberArray.js +6 -6
- package/dist/utils/queue.d.ts +11 -11
- package/dist/utils/queue.js +50 -50
- package/dist/utils/realpathSync.d.ts +2 -2
- package/dist/utils/realpathSync.js +12 -12
- package/dist/utils/wait.d.ts +2 -2
- package/dist/utils/wait.js +8 -8
- package/dist/utils/waitress.d.ts +21 -21
- package/dist/utils/waitress.js +61 -61
- package/dist/zcl/buffaloZcl.d.ts +41 -41
- package/dist/zcl/buffaloZcl.js +591 -591
- package/dist/zcl/definition/buffaloZclDataType.d.ts +17 -17
- package/dist/zcl/definition/buffaloZclDataType.js +20 -20
- package/dist/zcl/definition/cluster.d.ts +29 -29
- package/dist/zcl/definition/cluster.d.ts.map +1 -1
- package/dist/zcl/definition/cluster.js +5335 -5215
- package/dist/zcl/definition/cluster.js.map +1 -1
- package/dist/zcl/definition/dataType.d.ts +59 -59
- package/dist/zcl/definition/dataType.d.ts.map +1 -1
- package/dist/zcl/definition/dataType.js +64 -64
- package/dist/zcl/definition/dataType.js.map +1 -1
- package/dist/zcl/definition/direction.d.ts +5 -5
- package/dist/zcl/definition/direction.js +8 -8
- package/dist/zcl/definition/endpointDeviceType.d.ts +4 -4
- package/dist/zcl/definition/endpointDeviceType.js +15 -15
- package/dist/zcl/definition/foundation.d.ts +11 -11
- package/dist/zcl/definition/foundation.js +167 -167
- package/dist/zcl/definition/frameControl.d.ts +10 -10
- package/dist/zcl/definition/frameControl.js +2 -2
- package/dist/zcl/definition/frameType.d.ts +5 -5
- package/dist/zcl/definition/frameType.js +8 -8
- package/dist/zcl/definition/index.d.ts +13 -13
- package/dist/zcl/definition/index.js +51 -51
- package/dist/zcl/definition/manufacturerCode.d.ts +1074 -1074
- package/dist/zcl/definition/manufacturerCode.js +1079 -1079
- package/dist/zcl/definition/powerSource.d.ts +4 -4
- package/dist/zcl/definition/powerSource.js +12 -12
- package/dist/zcl/definition/status.d.ts +38 -38
- package/dist/zcl/definition/status.js +41 -41
- package/dist/zcl/definition/tstype.d.ts +16 -16
- package/dist/zcl/definition/tstype.js +2 -2
- package/dist/zcl/index.d.ts +15 -15
- package/dist/zcl/index.js +55 -55
- package/dist/zcl/tstype.d.ts +56 -56
- package/dist/zcl/tstype.js +10 -10
- package/dist/zcl/utils.d.ts +6 -6
- package/dist/zcl/utils.js +165 -165
- package/dist/zcl/zclFrame.d.ts +45 -45
- package/dist/zcl/zclFrame.js +347 -347
- package/dist/zcl/zclStatusError.d.ts +5 -5
- package/dist/zcl/zclStatusError.js +13 -13
- package/package.json +1 -1
- package/.github/ISSUE_TEMPLATE/config.yml +0 -5
- package/.github/dependabot.yml +0 -6
- package/.github/workflows/ci.yml +0 -40
- package/.github/workflows/release_please.yml +0 -20
- package/.github/workflows/stale.yml +0 -20
- package/.github/workflows/update_deps.yml +0 -29
|
@@ -1,608 +1,608 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const events_1 = __importDefault(require("events"));
|
|
30
|
-
const database_1 = __importDefault(require("./database"));
|
|
31
|
-
const adapter_1 = require("../adapter");
|
|
32
|
-
const model_1 = require("./model");
|
|
33
|
-
const helpers_1 = require("./helpers");
|
|
34
|
-
const Events = __importStar(require("./events"));
|
|
35
|
-
const tstype_1 = require("./tstype");
|
|
36
|
-
const debug_1 = __importDefault(require("debug"));
|
|
37
|
-
const fs_1 = __importDefault(require("fs"));
|
|
38
|
-
const zcl_1 = require("../zcl");
|
|
39
|
-
const touchlink_1 = __importDefault(require("./touchlink"));
|
|
40
|
-
const greenPower_1 = __importDefault(require("./greenPower"));
|
|
41
|
-
const utils_1 = require("../utils");
|
|
42
|
-
const assert_1 = __importDefault(require("assert"));
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
const mixin_deep_1 = __importDefault(require("mixin-deep"));
|
|
46
|
-
const group_1 = __importDefault(require("./model/group"));
|
|
47
|
-
async function catcho(func, errorMessage) {
|
|
48
|
-
try {
|
|
49
|
-
await func();
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
debug.error(`${errorMessage}: ${error}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
const DefaultOptions = {
|
|
56
|
-
network: {
|
|
57
|
-
networkKeyDistribute: false,
|
|
58
|
-
networkKey: [0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D],
|
|
59
|
-
panID: 0x1a62,
|
|
60
|
-
extendedPanID: [0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD],
|
|
61
|
-
channelList: [11],
|
|
62
|
-
},
|
|
63
|
-
serialPort: {},
|
|
64
|
-
databasePath: null,
|
|
65
|
-
databaseBackupPath: null,
|
|
66
|
-
backupPath: null,
|
|
67
|
-
adapter: { disableLED: false },
|
|
68
|
-
acceptJoiningDeviceHandler: null,
|
|
69
|
-
};
|
|
70
|
-
const debug = {
|
|
71
|
-
error: (0, debug_1.default)('zigbee-herdsman:controller:error'),
|
|
72
|
-
log: (0, debug_1.default)('zigbee-herdsman:controller:log'),
|
|
73
|
-
};
|
|
74
|
-
/**
|
|
75
|
-
* @noInheritDoc
|
|
76
|
-
*/
|
|
77
|
-
class Controller extends events_1.default.EventEmitter {
|
|
78
|
-
/**
|
|
79
|
-
* Create a controller
|
|
80
|
-
*
|
|
81
|
-
* To auto detect the port provide `null` for `options.serialPort.path`
|
|
82
|
-
*/
|
|
83
|
-
constructor(options, logger) {
|
|
84
|
-
super();
|
|
85
|
-
this.stopping = false;
|
|
86
|
-
this.options = (0, mixin_deep_1.default)(JSON.parse(JSON.stringify(DefaultOptions)), options);
|
|
87
|
-
this.logger = logger;
|
|
88
|
-
// Validate options
|
|
89
|
-
for (const channel of this.options.network.channelList) {
|
|
90
|
-
if (channel < 11 || channel > 26) {
|
|
91
|
-
throw new Error(`'${channel}' is an invalid channel, use a channel between 11 - 26.`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (!Array.isArray(this.options.network.networkKey) || this.options.network.networkKey.length !== 16) {
|
|
95
|
-
throw new Error(`Network key must be 16 digits long, got ${this.options.network.networkKey.length}.`);
|
|
96
|
-
}
|
|
97
|
-
if (!Array.isArray(this.options.network.extendedPanID) || this.options.network.extendedPanID.length !== 8) {
|
|
98
|
-
throw new Error(`ExtendedPanID must be 8 digits long, got ${this.options.network.extendedPanID.length}.`);
|
|
99
|
-
}
|
|
100
|
-
if (this.options.network.panID >= 0xFFFF || this.options.network.panID <= 0) {
|
|
101
|
-
throw new Error(`PanID must have a value of 0x0001 (1) - 0xFFFE (65534), ` +
|
|
102
|
-
`got ${this.options.network.panID}.`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Start the Herdsman controller
|
|
107
|
-
*/
|
|
108
|
-
async start() {
|
|
109
|
-
// Database (create end inject)
|
|
110
|
-
this.database = database_1.default.open(this.options.databasePath);
|
|
111
|
-
model_1.Entity.injectDatabase(this.database);
|
|
112
|
-
// Adapter (create and inject)
|
|
113
|
-
this.adapter = await adapter_1.Adapter.create(this.options.network, this.options.serialPort, this.options.backupPath, this.options.adapter, this.logger);
|
|
114
|
-
debug.log(`Starting with options '${JSON.stringify(this.options)}'`);
|
|
115
|
-
const startResult = await this.adapter.start();
|
|
116
|
-
debug.log(`Started with result '${startResult}'`);
|
|
117
|
-
model_1.Entity.injectAdapter(this.database.id, this.adapter);
|
|
118
|
-
// log injection
|
|
119
|
-
debug.log(`Injected database: ${this.database != null}, adapter: ${this.adapter != null}`);
|
|
120
|
-
this.greenPower = new greenPower_1.default(this.adapter);
|
|
121
|
-
this.greenPower.on(tstype_1.GreenPowerEvents.deviceJoined, this.onDeviceJoinedGreenPower.bind(this));
|
|
122
|
-
// Register adapter events
|
|
123
|
-
this.adapter.on(adapter_1.Events.Events.deviceJoined, this.onDeviceJoined.bind(this));
|
|
124
|
-
this.adapter.on(adapter_1.Events.Events.zclData, (data) => this.onZclOrRawData('zcl', data));
|
|
125
|
-
this.adapter.on(adapter_1.Events.Events.rawData, (data) => this.onZclOrRawData('raw', data));
|
|
126
|
-
this.adapter.on(adapter_1.Events.Events.disconnected, this.onAdapterDisconnected.bind(this));
|
|
127
|
-
this.adapter.on(adapter_1.Events.Events.deviceAnnounce, this.onDeviceAnnounce.bind(this));
|
|
128
|
-
this.adapter.on(adapter_1.Events.Events.deviceLeave, this.onDeviceLeave.bind(this));
|
|
129
|
-
this.adapter.on(adapter_1.Events.Events.networkAddress, this.onNetworkAddress.bind(this));
|
|
130
|
-
if (startResult === 'reset') {
|
|
131
|
-
if (this.options.databaseBackupPath && fs_1.default.existsSync(this.options.databasePath)) {
|
|
132
|
-
fs_1.default.copyFileSync(this.options.databasePath, this.options.databaseBackupPath);
|
|
133
|
-
}
|
|
134
|
-
debug.log('Clearing database...');
|
|
135
|
-
for (const group of group_1.default.allByDatabaseID(this.database.id)) {
|
|
136
|
-
group.removeFromDatabase();
|
|
137
|
-
}
|
|
138
|
-
for (const device of model_1.Device.allByDatabaseID(this.database.id)) {
|
|
139
|
-
device.removeFromDatabase();
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (startResult === 'reset' || (this.options.backupPath && !fs_1.default.existsSync(this.options.backupPath))) {
|
|
143
|
-
await this.backup();
|
|
144
|
-
}
|
|
145
|
-
// Add coordinator to the database if it is not there yet.
|
|
146
|
-
const coordinator = await this.adapter.getCoordinator();
|
|
147
|
-
if (model_1.Device.byType('Coordinator', this.database.id).length === 0) {
|
|
148
|
-
debug.log('No coordinator in database, querying...');
|
|
149
|
-
model_1.Device.create('Coordinator', coordinator.ieeeAddr, coordinator.networkAddress, coordinator.manufacturerID, undefined, undefined, undefined, true, coordinator.endpoints, this.database.id);
|
|
150
|
-
}
|
|
151
|
-
// Update coordinator ieeeAddr if changed, can happen due to e.g. reflashing
|
|
152
|
-
const databaseCoordinator = model_1.Device.byType('Coordinator', this.database.id)[0];
|
|
153
|
-
if (databaseCoordinator.ieeeAddr !== coordinator.ieeeAddr) {
|
|
154
|
-
debug.log(`Coordinator address changed, updating to '${coordinator.ieeeAddr}'`);
|
|
155
|
-
databaseCoordinator.changeIeeeAddress(coordinator.ieeeAddr);
|
|
156
|
-
}
|
|
157
|
-
// Set backup timer to 1 day.
|
|
158
|
-
this.backupTimer = setInterval(() => this.backup(), 86400000);
|
|
159
|
-
// Set database save timer to 1 hour.
|
|
160
|
-
this.databaseSaveTimer = setInterval(() => this.databaseSave(), 3600000);
|
|
161
|
-
this.touchlink = new touchlink_1.default(this.adapter);
|
|
162
|
-
return startResult;
|
|
163
|
-
}
|
|
164
|
-
async touchlinkIdentify(ieeeAddr, channel) {
|
|
165
|
-
await this.touchlink.identify(ieeeAddr, channel);
|
|
166
|
-
}
|
|
167
|
-
async touchlinkScan() {
|
|
168
|
-
return this.touchlink.scan();
|
|
169
|
-
}
|
|
170
|
-
async touchlinkFactoryReset(ieeeAddr, channel) {
|
|
171
|
-
return this.touchlink.factoryReset(ieeeAddr, channel);
|
|
172
|
-
}
|
|
173
|
-
async touchlinkFactoryResetFirst() {
|
|
174
|
-
return this.touchlink.factoryResetFirst();
|
|
175
|
-
}
|
|
176
|
-
async addInstallCode(installCode) {
|
|
177
|
-
const aqaraMatch = installCode.match(/^G\$M:.+\$A:(.+)\$I:(.+)$/);
|
|
178
|
-
let ieeeAddr, key;
|
|
179
|
-
if (aqaraMatch) {
|
|
180
|
-
ieeeAddr = aqaraMatch[1];
|
|
181
|
-
key = aqaraMatch[2];
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
(0, assert_1.default)(installCode.length === 95 || installCode.length === 91, `Unsupported install code, got ${installCode.length} chars, expected 95 or 91`);
|
|
185
|
-
const keyStart = installCode.length - (installCode.length === 95 ? 36 : 32);
|
|
186
|
-
ieeeAddr = installCode.substring(keyStart - 19, keyStart - 3);
|
|
187
|
-
key = installCode.substring(keyStart, installCode.length);
|
|
188
|
-
}
|
|
189
|
-
ieeeAddr = `0x${ieeeAddr}`;
|
|
190
|
-
key = Buffer.from(key.match(/.{1,2}/g).map(d => parseInt(d, 16)));
|
|
191
|
-
await this.adapter.addInstallCode(ieeeAddr, key);
|
|
192
|
-
}
|
|
193
|
-
async permitJoin(permit, device, time) {
|
|
194
|
-
await this.permitJoinInternal(permit, 'manual', device, time);
|
|
195
|
-
}
|
|
196
|
-
async permitJoinInternal(permit, reason, device, time) {
|
|
197
|
-
clearInterval(this.permitJoinNetworkClosedTimer);
|
|
198
|
-
clearInterval(this.permitJoinTimeoutTimer);
|
|
199
|
-
this.permitJoinNetworkClosedTimer = null;
|
|
200
|
-
this.permitJoinTimeoutTimer = null;
|
|
201
|
-
this.permitJoinTimeout = undefined;
|
|
202
|
-
if (permit) {
|
|
203
|
-
await this.adapter.permitJoin(254, !device ? null : device.networkAddress);
|
|
204
|
-
await this.greenPower.permitJoin(254, !device ? null : device.networkAddress);
|
|
205
|
-
// Zigbee 3 networks automatically close after max 255 seconds, keep network open.
|
|
206
|
-
this.permitJoinNetworkClosedTimer = setInterval(async () => {
|
|
207
|
-
await this.adapter.permitJoin(254, !device ? null : device.networkAddress);
|
|
208
|
-
await this.greenPower.permitJoin(254, !device ? null : device.networkAddress);
|
|
209
|
-
}, 200 * 1000);
|
|
210
|
-
if (typeof time === 'number') {
|
|
211
|
-
this.permitJoinTimeout = time;
|
|
212
|
-
this.permitJoinTimeoutTimer = setInterval(async () => {
|
|
213
|
-
this.permitJoinTimeout--;
|
|
214
|
-
if (this.permitJoinTimeout <= 0) {
|
|
215
|
-
await this.permitJoinInternal(false, 'timer_expired');
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
const data = { permitted: true, timeout: this.permitJoinTimeout, reason };
|
|
219
|
-
this.emit(Events.Events.permitJoinChanged, data);
|
|
220
|
-
}
|
|
221
|
-
}, 1000);
|
|
222
|
-
}
|
|
223
|
-
const data = { permitted: true, reason, timeout: this.permitJoinTimeout };
|
|
224
|
-
this.emit(Events.Events.permitJoinChanged, data);
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
debug.log('Disable joining');
|
|
228
|
-
await this.greenPower.permitJoin(0, null);
|
|
229
|
-
await this.adapter.permitJoin(0, null);
|
|
230
|
-
const data = { permitted: false, reason, timeout: this.permitJoinTimeout };
|
|
231
|
-
this.emit(Events.Events.permitJoinChanged, data);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
getPermitJoin() {
|
|
235
|
-
return this.permitJoinNetworkClosedTimer != null;
|
|
236
|
-
}
|
|
237
|
-
getPermitJoinTimeout() {
|
|
238
|
-
return this.permitJoinTimeout;
|
|
239
|
-
}
|
|
240
|
-
isStopping() {
|
|
241
|
-
return this.stopping;
|
|
242
|
-
}
|
|
243
|
-
async stop() {
|
|
244
|
-
this.stopping = true;
|
|
245
|
-
this.databaseSave();
|
|
246
|
-
// Unregister adapter events
|
|
247
|
-
this.adapter.removeAllListeners(adapter_1.Events.Events.deviceJoined);
|
|
248
|
-
this.adapter.removeAllListeners(adapter_1.Events.Events.zclData);
|
|
249
|
-
this.adapter.removeAllListeners(adapter_1.Events.Events.rawData);
|
|
250
|
-
this.adapter.removeAllListeners(adapter_1.Events.Events.disconnected);
|
|
251
|
-
this.adapter.removeAllListeners(adapter_1.Events.Events.deviceAnnounce);
|
|
252
|
-
this.adapter.removeAllListeners(adapter_1.Events.Events.deviceLeave);
|
|
253
|
-
await catcho(() => this.permitJoinInternal(false, 'manual'), "Failed to disable join on stop");
|
|
254
|
-
clearInterval(this.backupTimer);
|
|
255
|
-
clearInterval(this.databaseSaveTimer);
|
|
256
|
-
await this.backup();
|
|
257
|
-
await this.adapter.stop();
|
|
258
|
-
}
|
|
259
|
-
databaseSave() {
|
|
260
|
-
for (const device of model_1.Device.allByDatabaseID(this.database.id)) {
|
|
261
|
-
device.save(false);
|
|
262
|
-
}
|
|
263
|
-
for (const group of group_1.default.allByDatabaseID(this.database.id)) {
|
|
264
|
-
group.save(false);
|
|
265
|
-
}
|
|
266
|
-
this.database.write();
|
|
267
|
-
}
|
|
268
|
-
async backup() {
|
|
269
|
-
this.databaseSave();
|
|
270
|
-
if (this.options.backupPath && await this.adapter.supportsBackup()) {
|
|
271
|
-
debug.log('Creating coordinator backup');
|
|
272
|
-
const backup = await this.adapter.backup();
|
|
273
|
-
const unifiedBackup = await utils_1.BackupUtils.toUnifiedBackup(backup);
|
|
274
|
-
const tmpBackupPath = this.options.backupPath + '.tmp';
|
|
275
|
-
fs_1.default.writeFileSync(tmpBackupPath, JSON.stringify(unifiedBackup, null, 2));
|
|
276
|
-
fs_1.default.renameSync(tmpBackupPath, this.options.backupPath);
|
|
277
|
-
debug.log(`Wrote coordinator backup to '${this.options.backupPath}'`);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
async reset(type) {
|
|
281
|
-
await this.adapter.reset(type);
|
|
282
|
-
}
|
|
283
|
-
async getCoordinatorVersion() {
|
|
284
|
-
return this.adapter.getCoordinatorVersion();
|
|
285
|
-
}
|
|
286
|
-
async getNetworkParameters() {
|
|
287
|
-
// Cache network parameters as they don't change anymore after start.
|
|
288
|
-
if (!this.networkParametersCached) {
|
|
289
|
-
this.networkParametersCached = await this.adapter.getNetworkParameters();
|
|
290
|
-
}
|
|
291
|
-
return this.networkParametersCached;
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Get all devices
|
|
295
|
-
*/
|
|
296
|
-
getDevices() {
|
|
297
|
-
return model_1.Device.allByDatabaseID(this.database.id);
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Get all devices with a specific type
|
|
301
|
-
*/
|
|
302
|
-
getDevicesByType(type) {
|
|
303
|
-
return model_1.Device.byType(type, this.database.id);
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Get device by ieeeAddr
|
|
307
|
-
*/
|
|
308
|
-
getDeviceByIeeeAddr(ieeeAddr) {
|
|
309
|
-
return model_1.Device.byIeeeAddr(this.database.id, ieeeAddr);
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Get device by networkAddress
|
|
313
|
-
*/
|
|
314
|
-
getDeviceByNetworkAddress(networkAddress) {
|
|
315
|
-
return model_1.Device.byNetworkAddress(networkAddress, this.database.id);
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Get group by ID
|
|
319
|
-
*/
|
|
320
|
-
getGroupByID(groupID) {
|
|
321
|
-
return group_1.default.byGroupID(groupID, this.database.id);
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Get all groups
|
|
325
|
-
*/
|
|
326
|
-
getGroups() {
|
|
327
|
-
return group_1.default.allByDatabaseID(this.database.id);
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Create a Group
|
|
331
|
-
*/
|
|
332
|
-
createGroup(groupID) {
|
|
333
|
-
return group_1.default.create(groupID, this.database.id);
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Set transmit power of the adapter
|
|
337
|
-
*/
|
|
338
|
-
async setTransmitPower(value) {
|
|
339
|
-
return this.adapter.setTransmitPower(value);
|
|
340
|
-
}
|
|
341
|
-
onNetworkAddress(payload) {
|
|
342
|
-
debug.log(`Network address '${payload.ieeeAddr}'`);
|
|
343
|
-
const device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr);
|
|
344
|
-
if (!device) {
|
|
345
|
-
debug.log(`Network address is from unknown device '${payload.ieeeAddr}'`);
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'networkAddress' });
|
|
349
|
-
if (device.networkAddress !== payload.networkAddress) {
|
|
350
|
-
debug.log(`Device '${payload.ieeeAddr}' got new networkAddress '${payload.networkAddress}'`);
|
|
351
|
-
device.networkAddress = payload.networkAddress;
|
|
352
|
-
device.save();
|
|
353
|
-
const data = { device };
|
|
354
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceNetworkAddressChanged, data);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
onDeviceAnnounce(payload) {
|
|
358
|
-
debug.log(`Device announce '${payload.ieeeAddr}'`);
|
|
359
|
-
const device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr);
|
|
360
|
-
if (!device) {
|
|
361
|
-
debug.log(`Device announce is from unknown device '${payload.ieeeAddr}'`);
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
device.updateLastSeen();
|
|
365
|
-
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'deviceAnnounce' });
|
|
366
|
-
device.implicitCheckin();
|
|
367
|
-
if (device.networkAddress !== payload.networkAddress) {
|
|
368
|
-
debug.log(`Device '${payload.ieeeAddr}' announced with new networkAddress '${payload.networkAddress}'`);
|
|
369
|
-
device.networkAddress = payload.networkAddress;
|
|
370
|
-
device.save();
|
|
371
|
-
}
|
|
372
|
-
const data = { device };
|
|
373
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceAnnounce, data);
|
|
374
|
-
}
|
|
375
|
-
onDeviceLeave(payload) {
|
|
376
|
-
debug.log(`Device leave '${payload.ieeeAddr}'`);
|
|
377
|
-
const device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr);
|
|
378
|
-
if (device) {
|
|
379
|
-
debug.log(`Removing device from database '${payload.ieeeAddr}'`);
|
|
380
|
-
device.removeFromDatabase();
|
|
381
|
-
}
|
|
382
|
-
const data = { ieeeAddr: payload.ieeeAddr };
|
|
383
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceLeave, data);
|
|
384
|
-
}
|
|
385
|
-
async onAdapterDisconnected() {
|
|
386
|
-
debug.log(`Adapter disconnected'`);
|
|
387
|
-
await catcho(() => this.adapter.stop(), 'Failed to stop adapter on disconnect');
|
|
388
|
-
this.emit(Events.Events.adapterDisconnected);
|
|
389
|
-
}
|
|
390
|
-
async onDeviceJoinedGreenPower(payload) {
|
|
391
|
-
debug.log(`Green power device '${JSON.stringify(payload)}' joined`);
|
|
392
|
-
// Green power devices don't have an ieeeAddr, the sourceID is unique and static so use this.
|
|
393
|
-
let ieeeAddr = payload.sourceID.toString(16);
|
|
394
|
-
ieeeAddr = `0x${'0'.repeat(16 - ieeeAddr.length)}${ieeeAddr}`;
|
|
395
|
-
// Green power devices dont' have a modelID, create a modelID based on the deviceID (=type)
|
|
396
|
-
const modelID = `GreenPower_${payload.deviceID}`;
|
|
397
|
-
let device = model_1.Device.byIeeeAddr(this.database.id, ieeeAddr, true);
|
|
398
|
-
if (!device) {
|
|
399
|
-
debug.log(`New green power device '${ieeeAddr}' joined`);
|
|
400
|
-
debug.log(`Creating device '${ieeeAddr}'`);
|
|
401
|
-
device = model_1.Device.create('GreenPower', ieeeAddr, payload.networkAddress, null, undefined, undefined, modelID, true, [], this.database.id);
|
|
402
|
-
device.save();
|
|
403
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
404
|
-
const deviceInterviewPayload = { status: 'successful', device };
|
|
405
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, deviceInterviewPayload);
|
|
406
|
-
}
|
|
407
|
-
else if (device.isDeleted) {
|
|
408
|
-
debug.log(`Deleted green power device '${ieeeAddr}' joined`);
|
|
409
|
-
device.undelete(true);
|
|
410
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
411
|
-
const deviceInterviewPayload = { status: 'successful', device };
|
|
412
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, deviceInterviewPayload);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
selfAndDeviceEmit(device, event, data) {
|
|
416
|
-
device === null || device === void 0 ? void 0 : device.emit(event, data);
|
|
417
|
-
this.emit(event, data);
|
|
418
|
-
}
|
|
419
|
-
async onDeviceJoined(payload) {
|
|
420
|
-
debug.log(`Device '${payload.ieeeAddr}' joined`);
|
|
421
|
-
if (this.options.acceptJoiningDeviceHandler) {
|
|
422
|
-
if (!(await this.options.acceptJoiningDeviceHandler(payload.ieeeAddr))) {
|
|
423
|
-
debug.log(`Device '${payload.ieeeAddr}' rejected by handler, removing it`);
|
|
424
|
-
await catcho(() => this.adapter.removeDevice(payload.networkAddress, payload.ieeeAddr), 'Failed to remove rejected device');
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
debug.log(`Device '${payload.ieeeAddr}' accepted by handler`);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
let device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr, true);
|
|
432
|
-
if (!device) {
|
|
433
|
-
debug.log(`New device '${payload.ieeeAddr}' joined`);
|
|
434
|
-
debug.log(`Creating device '${payload.ieeeAddr}'`);
|
|
435
|
-
device = model_1.Device.create('Unknown', payload.ieeeAddr, payload.networkAddress, undefined, undefined, undefined, undefined, false, [], this.database.id);
|
|
436
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
437
|
-
}
|
|
438
|
-
else if (device.isDeleted) {
|
|
439
|
-
debug.log(`Delete device '${payload.ieeeAddr}' joined, undeleting`);
|
|
440
|
-
device.undelete();
|
|
441
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
442
|
-
}
|
|
443
|
-
if (device.networkAddress !== payload.networkAddress) {
|
|
444
|
-
debug.log(`Device '${payload.ieeeAddr}' is already in database with different networkAddress, ` +
|
|
445
|
-
`updating networkAddress`);
|
|
446
|
-
device.networkAddress = payload.networkAddress;
|
|
447
|
-
device.save();
|
|
448
|
-
}
|
|
449
|
-
device.updateLastSeen();
|
|
450
|
-
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'deviceJoined' });
|
|
451
|
-
device.implicitCheckin();
|
|
452
|
-
if (!device.interviewCompleted && !device.interviewing) {
|
|
453
|
-
const payloadStart = { status: 'started', device };
|
|
454
|
-
debug.log(`Interview '${device.ieeeAddr}' start`);
|
|
455
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, payloadStart);
|
|
456
|
-
try {
|
|
457
|
-
await device.interview();
|
|
458
|
-
debug.log(`Succesfully interviewed '${device.ieeeAddr}'`);
|
|
459
|
-
const event = { status: 'successful', device };
|
|
460
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, event);
|
|
461
|
-
}
|
|
462
|
-
catch (error) {
|
|
463
|
-
debug.error(`Interview failed for '${device.ieeeAddr} with error '${error}'`);
|
|
464
|
-
const event = { status: 'failed', device };
|
|
465
|
-
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, event);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
debug.log(`Not interviewing '${payload.ieeeAddr}', completed '${device.interviewCompleted}', ` +
|
|
470
|
-
`in progress '${device.interviewing}'`);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
isZclDataPayload(dataPayload, type) {
|
|
474
|
-
return type === 'zcl';
|
|
475
|
-
}
|
|
476
|
-
async onZclOrRawData(dataType, dataPayload) {
|
|
477
|
-
const logDataPayload = JSON.parse(JSON.stringify(dataPayload));
|
|
478
|
-
if (dataType === 'zcl') {
|
|
479
|
-
delete logDataPayload.frame.Cluster;
|
|
480
|
-
}
|
|
481
|
-
debug.log(`Received '${dataType}' data '${JSON.stringify(logDataPayload)}'`);
|
|
482
|
-
let gpDevice = null;
|
|
483
|
-
if (this.isZclDataPayload(dataPayload, dataType)) {
|
|
484
|
-
if (dataPayload.frame.Cluster.name === 'touchlink') {
|
|
485
|
-
// This is handled by touchlink
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
else if (dataPayload.frame.Cluster.name === 'greenPower') {
|
|
489
|
-
await this.greenPower.onZclGreenPowerData(dataPayload);
|
|
490
|
-
// lookup encapsulated gpDevice for further processing
|
|
491
|
-
gpDevice = model_1.Device.byNetworkAddress(dataPayload.frame.Payload.srcID & 0xFFFF, this.database.id);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
let device = gpDevice ? gpDevice : (typeof dataPayload.address === 'string'
|
|
495
|
-
? model_1.Device.byIeeeAddr(this.database.id, dataPayload.address)
|
|
496
|
-
: model_1.Device.byNetworkAddress(dataPayload.address, this.database.id));
|
|
497
|
-
/**
|
|
498
|
-
* Handling of re-transmitted Xiaomi messages.
|
|
499
|
-
* https://github.com/Koenkk/zigbee2mqtt/issues/1238
|
|
500
|
-
* https://github.com/Koenkk/zigbee2mqtt/issues/3592
|
|
501
|
-
*
|
|
502
|
-
* Some Xiaomi router devices re-transmit messages from Xiaomi end devices.
|
|
503
|
-
* The network address of these message is set to the one of the Xiaomi router.
|
|
504
|
-
* Therefore it looks like if the message came from the Xiaomi router, while in
|
|
505
|
-
* fact it came from the end device.
|
|
506
|
-
* Handling these message would result in false state updates.
|
|
507
|
-
* The group ID attribute of these message defines the network address of the end device.
|
|
508
|
-
*/
|
|
509
|
-
if ((device === null || device === void 0 ? void 0 : device.manufacturerName) === 'LUMI' && (device === null || device === void 0 ? void 0 : device.type) == 'Router' && dataPayload.groupID) {
|
|
510
|
-
debug.log(`Handling re-transmitted Xiaomi message ${device.networkAddress} -> ${dataPayload.groupID}`);
|
|
511
|
-
device = model_1.Device.byNetworkAddress(dataPayload.groupID, this.database.id);
|
|
512
|
-
}
|
|
513
|
-
if (!device) {
|
|
514
|
-
debug.log(`'${dataType}' data is from unknown device with address '${dataPayload.address}', ` +
|
|
515
|
-
`skipping...`);
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
device.updateLastSeen();
|
|
519
|
-
device.implicitCheckin();
|
|
520
|
-
device.linkquality = dataPayload.linkquality;
|
|
521
|
-
let endpoint = device.getEndpoint(dataPayload.endpoint);
|
|
522
|
-
if (!endpoint) {
|
|
523
|
-
debug.log(`'${dataType}' data is from unknown endpoint '${dataPayload.endpoint}' from device with ` +
|
|
524
|
-
`network address '${dataPayload.address}', creating it...`);
|
|
525
|
-
endpoint = device.createEndpoint(dataPayload.endpoint);
|
|
526
|
-
}
|
|
527
|
-
// Parse command for event
|
|
528
|
-
let type = undefined;
|
|
529
|
-
let data;
|
|
530
|
-
let clusterName = undefined;
|
|
531
|
-
const meta = {};
|
|
532
|
-
if (this.isZclDataPayload(dataPayload, dataType)) {
|
|
533
|
-
const frame = dataPayload.frame;
|
|
534
|
-
const command = frame.getCommand();
|
|
535
|
-
clusterName = frame.Cluster.name;
|
|
536
|
-
meta.zclTransactionSequenceNumber = frame.Header.transactionSequenceNumber;
|
|
537
|
-
meta.manufacturerCode = frame.Header.manufacturerCode;
|
|
538
|
-
meta.frameControl = frame.Header.frameControl;
|
|
539
|
-
if (frame.isGlobal()) {
|
|
540
|
-
if (frame.isCommand('report')) {
|
|
541
|
-
type = 'attributeReport';
|
|
542
|
-
data = helpers_1.ZclFrameConverter.attributeKeyValue(dataPayload.frame);
|
|
543
|
-
}
|
|
544
|
-
else if (frame.isCommand('read')) {
|
|
545
|
-
type = 'read';
|
|
546
|
-
data = helpers_1.ZclFrameConverter.attributeList(dataPayload.frame);
|
|
547
|
-
}
|
|
548
|
-
else if (frame.isCommand('write')) {
|
|
549
|
-
type = 'write';
|
|
550
|
-
data = helpers_1.ZclFrameConverter.attributeKeyValue(dataPayload.frame);
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
/* istanbul ignore else */
|
|
554
|
-
if (frame.isCommand('readRsp')) {
|
|
555
|
-
type = 'readResponse';
|
|
556
|
-
data = helpers_1.ZclFrameConverter.attributeKeyValue(dataPayload.frame);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
/* istanbul ignore else */
|
|
562
|
-
if (frame.isSpecific()) {
|
|
563
|
-
if (Events.CommandsLookup[command.name]) {
|
|
564
|
-
type = Events.CommandsLookup[command.name];
|
|
565
|
-
data = dataPayload.frame.Payload;
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
debug.log(`Skipping command '${command.name}' because it is missing from the lookup`);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
if (type === 'readResponse' || type === 'attributeReport') {
|
|
573
|
-
// Some device report, e.g. it's modelID through a readResponse or attributeReport
|
|
574
|
-
for (const [key, value] of Object.entries(data)) {
|
|
575
|
-
const property = model_1.Device.ReportablePropertiesMapping[key];
|
|
576
|
-
if (property && !device[property.key]) {
|
|
577
|
-
property.set(value, device);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
endpoint.saveClusterAttributeKeyValue(frame.Cluster.ID, data);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
type = 'raw';
|
|
585
|
-
data = dataPayload.data;
|
|
586
|
-
const name = zcl_1.Utils.getCluster(dataPayload.clusterID).name;
|
|
587
|
-
clusterName = Number.isNaN(Number(name)) ? name : Number(name);
|
|
588
|
-
}
|
|
589
|
-
if (type && data) {
|
|
590
|
-
const endpoint = device.getEndpoint(dataPayload.endpoint);
|
|
591
|
-
const linkquality = dataPayload.linkquality;
|
|
592
|
-
const groupID = dataPayload.groupID;
|
|
593
|
-
const eventData = {
|
|
594
|
-
type: type, device, endpoint, data, linkquality, groupID, cluster: clusterName, meta
|
|
595
|
-
};
|
|
596
|
-
this.selfAndDeviceEmit(device, Events.Events.message, eventData);
|
|
597
|
-
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'messageEmitted' });
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'messageNonEmitted' });
|
|
601
|
-
}
|
|
602
|
-
if (this.isZclDataPayload(dataPayload, dataType)) {
|
|
603
|
-
device.onZclData(dataPayload, endpoint);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
exports.default = Controller;
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const events_1 = __importDefault(require("events"));
|
|
30
|
+
const database_1 = __importDefault(require("./database"));
|
|
31
|
+
const adapter_1 = require("../adapter");
|
|
32
|
+
const model_1 = require("./model");
|
|
33
|
+
const helpers_1 = require("./helpers");
|
|
34
|
+
const Events = __importStar(require("./events"));
|
|
35
|
+
const tstype_1 = require("./tstype");
|
|
36
|
+
const debug_1 = __importDefault(require("debug"));
|
|
37
|
+
const fs_1 = __importDefault(require("fs"));
|
|
38
|
+
const zcl_1 = require("../zcl");
|
|
39
|
+
const touchlink_1 = __importDefault(require("./touchlink"));
|
|
40
|
+
const greenPower_1 = __importDefault(require("./greenPower"));
|
|
41
|
+
const utils_1 = require("../utils");
|
|
42
|
+
const assert_1 = __importDefault(require("assert"));
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
44
|
+
// @ts-ignore
|
|
45
|
+
const mixin_deep_1 = __importDefault(require("mixin-deep"));
|
|
46
|
+
const group_1 = __importDefault(require("./model/group"));
|
|
47
|
+
async function catcho(func, errorMessage) {
|
|
48
|
+
try {
|
|
49
|
+
await func();
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
debug.error(`${errorMessage}: ${error}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const DefaultOptions = {
|
|
56
|
+
network: {
|
|
57
|
+
networkKeyDistribute: false,
|
|
58
|
+
networkKey: [0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D],
|
|
59
|
+
panID: 0x1a62,
|
|
60
|
+
extendedPanID: [0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD],
|
|
61
|
+
channelList: [11],
|
|
62
|
+
},
|
|
63
|
+
serialPort: {},
|
|
64
|
+
databasePath: null,
|
|
65
|
+
databaseBackupPath: null,
|
|
66
|
+
backupPath: null,
|
|
67
|
+
adapter: { disableLED: false },
|
|
68
|
+
acceptJoiningDeviceHandler: null,
|
|
69
|
+
};
|
|
70
|
+
const debug = {
|
|
71
|
+
error: (0, debug_1.default)('zigbee-herdsman:controller:error'),
|
|
72
|
+
log: (0, debug_1.default)('zigbee-herdsman:controller:log'),
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* @noInheritDoc
|
|
76
|
+
*/
|
|
77
|
+
class Controller extends events_1.default.EventEmitter {
|
|
78
|
+
/**
|
|
79
|
+
* Create a controller
|
|
80
|
+
*
|
|
81
|
+
* To auto detect the port provide `null` for `options.serialPort.path`
|
|
82
|
+
*/
|
|
83
|
+
constructor(options, logger) {
|
|
84
|
+
super();
|
|
85
|
+
this.stopping = false;
|
|
86
|
+
this.options = (0, mixin_deep_1.default)(JSON.parse(JSON.stringify(DefaultOptions)), options);
|
|
87
|
+
this.logger = logger;
|
|
88
|
+
// Validate options
|
|
89
|
+
for (const channel of this.options.network.channelList) {
|
|
90
|
+
if (channel < 11 || channel > 26) {
|
|
91
|
+
throw new Error(`'${channel}' is an invalid channel, use a channel between 11 - 26.`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (!Array.isArray(this.options.network.networkKey) || this.options.network.networkKey.length !== 16) {
|
|
95
|
+
throw new Error(`Network key must be 16 digits long, got ${this.options.network.networkKey.length}.`);
|
|
96
|
+
}
|
|
97
|
+
if (!Array.isArray(this.options.network.extendedPanID) || this.options.network.extendedPanID.length !== 8) {
|
|
98
|
+
throw new Error(`ExtendedPanID must be 8 digits long, got ${this.options.network.extendedPanID.length}.`);
|
|
99
|
+
}
|
|
100
|
+
if (this.options.network.panID >= 0xFFFF || this.options.network.panID <= 0) {
|
|
101
|
+
throw new Error(`PanID must have a value of 0x0001 (1) - 0xFFFE (65534), ` +
|
|
102
|
+
`got ${this.options.network.panID}.`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Start the Herdsman controller
|
|
107
|
+
*/
|
|
108
|
+
async start() {
|
|
109
|
+
// Database (create end inject)
|
|
110
|
+
this.database = database_1.default.open(this.options.databasePath);
|
|
111
|
+
model_1.Entity.injectDatabase(this.database);
|
|
112
|
+
// Adapter (create and inject)
|
|
113
|
+
this.adapter = await adapter_1.Adapter.create(this.options.network, this.options.serialPort, this.options.backupPath, this.options.adapter, this.logger);
|
|
114
|
+
debug.log(`Starting with options '${JSON.stringify(this.options)}'`);
|
|
115
|
+
const startResult = await this.adapter.start();
|
|
116
|
+
debug.log(`Started with result '${startResult}'`);
|
|
117
|
+
model_1.Entity.injectAdapter(this.database.id, this.adapter);
|
|
118
|
+
// log injection
|
|
119
|
+
debug.log(`Injected database: ${this.database != null}, adapter: ${this.adapter != null}`);
|
|
120
|
+
this.greenPower = new greenPower_1.default(this.adapter);
|
|
121
|
+
this.greenPower.on(tstype_1.GreenPowerEvents.deviceJoined, this.onDeviceJoinedGreenPower.bind(this));
|
|
122
|
+
// Register adapter events
|
|
123
|
+
this.adapter.on(adapter_1.Events.Events.deviceJoined, this.onDeviceJoined.bind(this));
|
|
124
|
+
this.adapter.on(adapter_1.Events.Events.zclData, (data) => this.onZclOrRawData('zcl', data));
|
|
125
|
+
this.adapter.on(adapter_1.Events.Events.rawData, (data) => this.onZclOrRawData('raw', data));
|
|
126
|
+
this.adapter.on(adapter_1.Events.Events.disconnected, this.onAdapterDisconnected.bind(this));
|
|
127
|
+
this.adapter.on(adapter_1.Events.Events.deviceAnnounce, this.onDeviceAnnounce.bind(this));
|
|
128
|
+
this.adapter.on(adapter_1.Events.Events.deviceLeave, this.onDeviceLeave.bind(this));
|
|
129
|
+
this.adapter.on(adapter_1.Events.Events.networkAddress, this.onNetworkAddress.bind(this));
|
|
130
|
+
if (startResult === 'reset') {
|
|
131
|
+
if (this.options.databaseBackupPath && fs_1.default.existsSync(this.options.databasePath)) {
|
|
132
|
+
fs_1.default.copyFileSync(this.options.databasePath, this.options.databaseBackupPath);
|
|
133
|
+
}
|
|
134
|
+
debug.log('Clearing database...');
|
|
135
|
+
for (const group of group_1.default.allByDatabaseID(this.database.id)) {
|
|
136
|
+
group.removeFromDatabase();
|
|
137
|
+
}
|
|
138
|
+
for (const device of model_1.Device.allByDatabaseID(this.database.id)) {
|
|
139
|
+
device.removeFromDatabase();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (startResult === 'reset' || (this.options.backupPath && !fs_1.default.existsSync(this.options.backupPath))) {
|
|
143
|
+
await this.backup();
|
|
144
|
+
}
|
|
145
|
+
// Add coordinator to the database if it is not there yet.
|
|
146
|
+
const coordinator = await this.adapter.getCoordinator();
|
|
147
|
+
if (model_1.Device.byType('Coordinator', this.database.id).length === 0) {
|
|
148
|
+
debug.log('No coordinator in database, querying...');
|
|
149
|
+
model_1.Device.create('Coordinator', coordinator.ieeeAddr, coordinator.networkAddress, coordinator.manufacturerID, undefined, undefined, undefined, true, coordinator.endpoints, this.database.id);
|
|
150
|
+
}
|
|
151
|
+
// Update coordinator ieeeAddr if changed, can happen due to e.g. reflashing
|
|
152
|
+
const databaseCoordinator = model_1.Device.byType('Coordinator', this.database.id)[0];
|
|
153
|
+
if (databaseCoordinator.ieeeAddr !== coordinator.ieeeAddr) {
|
|
154
|
+
debug.log(`Coordinator address changed, updating to '${coordinator.ieeeAddr}'`);
|
|
155
|
+
databaseCoordinator.changeIeeeAddress(coordinator.ieeeAddr);
|
|
156
|
+
}
|
|
157
|
+
// Set backup timer to 1 day.
|
|
158
|
+
this.backupTimer = setInterval(() => this.backup(), 86400000);
|
|
159
|
+
// Set database save timer to 1 hour.
|
|
160
|
+
this.databaseSaveTimer = setInterval(() => this.databaseSave(), 3600000);
|
|
161
|
+
this.touchlink = new touchlink_1.default(this.adapter);
|
|
162
|
+
return startResult;
|
|
163
|
+
}
|
|
164
|
+
async touchlinkIdentify(ieeeAddr, channel) {
|
|
165
|
+
await this.touchlink.identify(ieeeAddr, channel);
|
|
166
|
+
}
|
|
167
|
+
async touchlinkScan() {
|
|
168
|
+
return this.touchlink.scan();
|
|
169
|
+
}
|
|
170
|
+
async touchlinkFactoryReset(ieeeAddr, channel) {
|
|
171
|
+
return this.touchlink.factoryReset(ieeeAddr, channel);
|
|
172
|
+
}
|
|
173
|
+
async touchlinkFactoryResetFirst() {
|
|
174
|
+
return this.touchlink.factoryResetFirst();
|
|
175
|
+
}
|
|
176
|
+
async addInstallCode(installCode) {
|
|
177
|
+
const aqaraMatch = installCode.match(/^G\$M:.+\$A:(.+)\$I:(.+)$/);
|
|
178
|
+
let ieeeAddr, key;
|
|
179
|
+
if (aqaraMatch) {
|
|
180
|
+
ieeeAddr = aqaraMatch[1];
|
|
181
|
+
key = aqaraMatch[2];
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
(0, assert_1.default)(installCode.length === 95 || installCode.length === 91, `Unsupported install code, got ${installCode.length} chars, expected 95 or 91`);
|
|
185
|
+
const keyStart = installCode.length - (installCode.length === 95 ? 36 : 32);
|
|
186
|
+
ieeeAddr = installCode.substring(keyStart - 19, keyStart - 3);
|
|
187
|
+
key = installCode.substring(keyStart, installCode.length);
|
|
188
|
+
}
|
|
189
|
+
ieeeAddr = `0x${ieeeAddr}`;
|
|
190
|
+
key = Buffer.from(key.match(/.{1,2}/g).map(d => parseInt(d, 16)));
|
|
191
|
+
await this.adapter.addInstallCode(ieeeAddr, key);
|
|
192
|
+
}
|
|
193
|
+
async permitJoin(permit, device, time) {
|
|
194
|
+
await this.permitJoinInternal(permit, 'manual', device, time);
|
|
195
|
+
}
|
|
196
|
+
async permitJoinInternal(permit, reason, device, time) {
|
|
197
|
+
clearInterval(this.permitJoinNetworkClosedTimer);
|
|
198
|
+
clearInterval(this.permitJoinTimeoutTimer);
|
|
199
|
+
this.permitJoinNetworkClosedTimer = null;
|
|
200
|
+
this.permitJoinTimeoutTimer = null;
|
|
201
|
+
this.permitJoinTimeout = undefined;
|
|
202
|
+
if (permit) {
|
|
203
|
+
await this.adapter.permitJoin(254, !device ? null : device.networkAddress);
|
|
204
|
+
await this.greenPower.permitJoin(254, !device ? null : device.networkAddress);
|
|
205
|
+
// Zigbee 3 networks automatically close after max 255 seconds, keep network open.
|
|
206
|
+
this.permitJoinNetworkClosedTimer = setInterval(async () => {
|
|
207
|
+
await this.adapter.permitJoin(254, !device ? null : device.networkAddress);
|
|
208
|
+
await this.greenPower.permitJoin(254, !device ? null : device.networkAddress);
|
|
209
|
+
}, 200 * 1000);
|
|
210
|
+
if (typeof time === 'number') {
|
|
211
|
+
this.permitJoinTimeout = time;
|
|
212
|
+
this.permitJoinTimeoutTimer = setInterval(async () => {
|
|
213
|
+
this.permitJoinTimeout--;
|
|
214
|
+
if (this.permitJoinTimeout <= 0) {
|
|
215
|
+
await this.permitJoinInternal(false, 'timer_expired');
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const data = { permitted: true, timeout: this.permitJoinTimeout, reason };
|
|
219
|
+
this.emit(Events.Events.permitJoinChanged, data);
|
|
220
|
+
}
|
|
221
|
+
}, 1000);
|
|
222
|
+
}
|
|
223
|
+
const data = { permitted: true, reason, timeout: this.permitJoinTimeout };
|
|
224
|
+
this.emit(Events.Events.permitJoinChanged, data);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
debug.log('Disable joining');
|
|
228
|
+
await this.greenPower.permitJoin(0, null);
|
|
229
|
+
await this.adapter.permitJoin(0, null);
|
|
230
|
+
const data = { permitted: false, reason, timeout: this.permitJoinTimeout };
|
|
231
|
+
this.emit(Events.Events.permitJoinChanged, data);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
getPermitJoin() {
|
|
235
|
+
return this.permitJoinNetworkClosedTimer != null;
|
|
236
|
+
}
|
|
237
|
+
getPermitJoinTimeout() {
|
|
238
|
+
return this.permitJoinTimeout;
|
|
239
|
+
}
|
|
240
|
+
isStopping() {
|
|
241
|
+
return this.stopping;
|
|
242
|
+
}
|
|
243
|
+
async stop() {
|
|
244
|
+
this.stopping = true;
|
|
245
|
+
this.databaseSave();
|
|
246
|
+
// Unregister adapter events
|
|
247
|
+
this.adapter.removeAllListeners(adapter_1.Events.Events.deviceJoined);
|
|
248
|
+
this.adapter.removeAllListeners(adapter_1.Events.Events.zclData);
|
|
249
|
+
this.adapter.removeAllListeners(adapter_1.Events.Events.rawData);
|
|
250
|
+
this.adapter.removeAllListeners(adapter_1.Events.Events.disconnected);
|
|
251
|
+
this.adapter.removeAllListeners(adapter_1.Events.Events.deviceAnnounce);
|
|
252
|
+
this.adapter.removeAllListeners(adapter_1.Events.Events.deviceLeave);
|
|
253
|
+
await catcho(() => this.permitJoinInternal(false, 'manual'), "Failed to disable join on stop");
|
|
254
|
+
clearInterval(this.backupTimer);
|
|
255
|
+
clearInterval(this.databaseSaveTimer);
|
|
256
|
+
await this.backup();
|
|
257
|
+
await this.adapter.stop();
|
|
258
|
+
}
|
|
259
|
+
databaseSave() {
|
|
260
|
+
for (const device of model_1.Device.allByDatabaseID(this.database.id)) {
|
|
261
|
+
device.save(false);
|
|
262
|
+
}
|
|
263
|
+
for (const group of group_1.default.allByDatabaseID(this.database.id)) {
|
|
264
|
+
group.save(false);
|
|
265
|
+
}
|
|
266
|
+
this.database.write();
|
|
267
|
+
}
|
|
268
|
+
async backup() {
|
|
269
|
+
this.databaseSave();
|
|
270
|
+
if (this.options.backupPath && await this.adapter.supportsBackup()) {
|
|
271
|
+
debug.log('Creating coordinator backup');
|
|
272
|
+
const backup = await this.adapter.backup();
|
|
273
|
+
const unifiedBackup = await utils_1.BackupUtils.toUnifiedBackup(backup);
|
|
274
|
+
const tmpBackupPath = this.options.backupPath + '.tmp';
|
|
275
|
+
fs_1.default.writeFileSync(tmpBackupPath, JSON.stringify(unifiedBackup, null, 2));
|
|
276
|
+
fs_1.default.renameSync(tmpBackupPath, this.options.backupPath);
|
|
277
|
+
debug.log(`Wrote coordinator backup to '${this.options.backupPath}'`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async reset(type) {
|
|
281
|
+
await this.adapter.reset(type);
|
|
282
|
+
}
|
|
283
|
+
async getCoordinatorVersion() {
|
|
284
|
+
return this.adapter.getCoordinatorVersion();
|
|
285
|
+
}
|
|
286
|
+
async getNetworkParameters() {
|
|
287
|
+
// Cache network parameters as they don't change anymore after start.
|
|
288
|
+
if (!this.networkParametersCached) {
|
|
289
|
+
this.networkParametersCached = await this.adapter.getNetworkParameters();
|
|
290
|
+
}
|
|
291
|
+
return this.networkParametersCached;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get all devices
|
|
295
|
+
*/
|
|
296
|
+
getDevices() {
|
|
297
|
+
return model_1.Device.allByDatabaseID(this.database.id);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get all devices with a specific type
|
|
301
|
+
*/
|
|
302
|
+
getDevicesByType(type) {
|
|
303
|
+
return model_1.Device.byType(type, this.database.id);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get device by ieeeAddr
|
|
307
|
+
*/
|
|
308
|
+
getDeviceByIeeeAddr(ieeeAddr) {
|
|
309
|
+
return model_1.Device.byIeeeAddr(this.database.id, ieeeAddr);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Get device by networkAddress
|
|
313
|
+
*/
|
|
314
|
+
getDeviceByNetworkAddress(networkAddress) {
|
|
315
|
+
return model_1.Device.byNetworkAddress(networkAddress, this.database.id);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Get group by ID
|
|
319
|
+
*/
|
|
320
|
+
getGroupByID(groupID) {
|
|
321
|
+
return group_1.default.byGroupID(groupID, this.database.id);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Get all groups
|
|
325
|
+
*/
|
|
326
|
+
getGroups() {
|
|
327
|
+
return group_1.default.allByDatabaseID(this.database.id);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Create a Group
|
|
331
|
+
*/
|
|
332
|
+
createGroup(groupID) {
|
|
333
|
+
return group_1.default.create(groupID, this.database.id);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Set transmit power of the adapter
|
|
337
|
+
*/
|
|
338
|
+
async setTransmitPower(value) {
|
|
339
|
+
return this.adapter.setTransmitPower(value);
|
|
340
|
+
}
|
|
341
|
+
onNetworkAddress(payload) {
|
|
342
|
+
debug.log(`Network address '${payload.ieeeAddr}'`);
|
|
343
|
+
const device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr);
|
|
344
|
+
if (!device) {
|
|
345
|
+
debug.log(`Network address is from unknown device '${payload.ieeeAddr}'`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'networkAddress' });
|
|
349
|
+
if (device.networkAddress !== payload.networkAddress) {
|
|
350
|
+
debug.log(`Device '${payload.ieeeAddr}' got new networkAddress '${payload.networkAddress}'`);
|
|
351
|
+
device.networkAddress = payload.networkAddress;
|
|
352
|
+
device.save();
|
|
353
|
+
const data = { device };
|
|
354
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceNetworkAddressChanged, data);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
onDeviceAnnounce(payload) {
|
|
358
|
+
debug.log(`Device announce '${payload.ieeeAddr}'`);
|
|
359
|
+
const device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr);
|
|
360
|
+
if (!device) {
|
|
361
|
+
debug.log(`Device announce is from unknown device '${payload.ieeeAddr}'`);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
device.updateLastSeen();
|
|
365
|
+
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'deviceAnnounce' });
|
|
366
|
+
device.implicitCheckin();
|
|
367
|
+
if (device.networkAddress !== payload.networkAddress) {
|
|
368
|
+
debug.log(`Device '${payload.ieeeAddr}' announced with new networkAddress '${payload.networkAddress}'`);
|
|
369
|
+
device.networkAddress = payload.networkAddress;
|
|
370
|
+
device.save();
|
|
371
|
+
}
|
|
372
|
+
const data = { device };
|
|
373
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceAnnounce, data);
|
|
374
|
+
}
|
|
375
|
+
onDeviceLeave(payload) {
|
|
376
|
+
debug.log(`Device leave '${payload.ieeeAddr}'`);
|
|
377
|
+
const device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr);
|
|
378
|
+
if (device) {
|
|
379
|
+
debug.log(`Removing device from database '${payload.ieeeAddr}'`);
|
|
380
|
+
device.removeFromDatabase();
|
|
381
|
+
}
|
|
382
|
+
const data = { ieeeAddr: payload.ieeeAddr };
|
|
383
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceLeave, data);
|
|
384
|
+
}
|
|
385
|
+
async onAdapterDisconnected() {
|
|
386
|
+
debug.log(`Adapter disconnected'`);
|
|
387
|
+
await catcho(() => this.adapter.stop(), 'Failed to stop adapter on disconnect');
|
|
388
|
+
this.emit(Events.Events.adapterDisconnected);
|
|
389
|
+
}
|
|
390
|
+
async onDeviceJoinedGreenPower(payload) {
|
|
391
|
+
debug.log(`Green power device '${JSON.stringify(payload)}' joined`);
|
|
392
|
+
// Green power devices don't have an ieeeAddr, the sourceID is unique and static so use this.
|
|
393
|
+
let ieeeAddr = payload.sourceID.toString(16);
|
|
394
|
+
ieeeAddr = `0x${'0'.repeat(16 - ieeeAddr.length)}${ieeeAddr}`;
|
|
395
|
+
// Green power devices dont' have a modelID, create a modelID based on the deviceID (=type)
|
|
396
|
+
const modelID = `GreenPower_${payload.deviceID}`;
|
|
397
|
+
let device = model_1.Device.byIeeeAddr(this.database.id, ieeeAddr, true);
|
|
398
|
+
if (!device) {
|
|
399
|
+
debug.log(`New green power device '${ieeeAddr}' joined`);
|
|
400
|
+
debug.log(`Creating device '${ieeeAddr}'`);
|
|
401
|
+
device = model_1.Device.create('GreenPower', ieeeAddr, payload.networkAddress, null, undefined, undefined, modelID, true, [], this.database.id);
|
|
402
|
+
device.save();
|
|
403
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
404
|
+
const deviceInterviewPayload = { status: 'successful', device };
|
|
405
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, deviceInterviewPayload);
|
|
406
|
+
}
|
|
407
|
+
else if (device.isDeleted) {
|
|
408
|
+
debug.log(`Deleted green power device '${ieeeAddr}' joined`);
|
|
409
|
+
device.undelete(true);
|
|
410
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
411
|
+
const deviceInterviewPayload = { status: 'successful', device };
|
|
412
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, deviceInterviewPayload);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
selfAndDeviceEmit(device, event, data) {
|
|
416
|
+
device === null || device === void 0 ? void 0 : device.emit(event, data);
|
|
417
|
+
this.emit(event, data);
|
|
418
|
+
}
|
|
419
|
+
async onDeviceJoined(payload) {
|
|
420
|
+
debug.log(`Device '${payload.ieeeAddr}' joined`);
|
|
421
|
+
if (this.options.acceptJoiningDeviceHandler) {
|
|
422
|
+
if (!(await this.options.acceptJoiningDeviceHandler(payload.ieeeAddr))) {
|
|
423
|
+
debug.log(`Device '${payload.ieeeAddr}' rejected by handler, removing it`);
|
|
424
|
+
await catcho(() => this.adapter.removeDevice(payload.networkAddress, payload.ieeeAddr), 'Failed to remove rejected device');
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
debug.log(`Device '${payload.ieeeAddr}' accepted by handler`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
let device = model_1.Device.byIeeeAddr(this.database.id, payload.ieeeAddr, true);
|
|
432
|
+
if (!device) {
|
|
433
|
+
debug.log(`New device '${payload.ieeeAddr}' joined`);
|
|
434
|
+
debug.log(`Creating device '${payload.ieeeAddr}'`);
|
|
435
|
+
device = model_1.Device.create('Unknown', payload.ieeeAddr, payload.networkAddress, undefined, undefined, undefined, undefined, false, [], this.database.id);
|
|
436
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
437
|
+
}
|
|
438
|
+
else if (device.isDeleted) {
|
|
439
|
+
debug.log(`Delete device '${payload.ieeeAddr}' joined, undeleting`);
|
|
440
|
+
device.undelete();
|
|
441
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceJoined, { device });
|
|
442
|
+
}
|
|
443
|
+
if (device.networkAddress !== payload.networkAddress) {
|
|
444
|
+
debug.log(`Device '${payload.ieeeAddr}' is already in database with different networkAddress, ` +
|
|
445
|
+
`updating networkAddress`);
|
|
446
|
+
device.networkAddress = payload.networkAddress;
|
|
447
|
+
device.save();
|
|
448
|
+
}
|
|
449
|
+
device.updateLastSeen();
|
|
450
|
+
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'deviceJoined' });
|
|
451
|
+
device.implicitCheckin();
|
|
452
|
+
if (!device.interviewCompleted && !device.interviewing) {
|
|
453
|
+
const payloadStart = { status: 'started', device };
|
|
454
|
+
debug.log(`Interview '${device.ieeeAddr}' start`);
|
|
455
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, payloadStart);
|
|
456
|
+
try {
|
|
457
|
+
await device.interview();
|
|
458
|
+
debug.log(`Succesfully interviewed '${device.ieeeAddr}'`);
|
|
459
|
+
const event = { status: 'successful', device };
|
|
460
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, event);
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
debug.error(`Interview failed for '${device.ieeeAddr} with error '${error}'`);
|
|
464
|
+
const event = { status: 'failed', device };
|
|
465
|
+
this.selfAndDeviceEmit(device, Events.Events.deviceInterview, event);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
debug.log(`Not interviewing '${payload.ieeeAddr}', completed '${device.interviewCompleted}', ` +
|
|
470
|
+
`in progress '${device.interviewing}'`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
isZclDataPayload(dataPayload, type) {
|
|
474
|
+
return type === 'zcl';
|
|
475
|
+
}
|
|
476
|
+
async onZclOrRawData(dataType, dataPayload) {
|
|
477
|
+
const logDataPayload = JSON.parse(JSON.stringify(dataPayload));
|
|
478
|
+
if (dataType === 'zcl') {
|
|
479
|
+
delete logDataPayload.frame.Cluster;
|
|
480
|
+
}
|
|
481
|
+
debug.log(`Received '${dataType}' data '${JSON.stringify(logDataPayload)}'`);
|
|
482
|
+
let gpDevice = null;
|
|
483
|
+
if (this.isZclDataPayload(dataPayload, dataType)) {
|
|
484
|
+
if (dataPayload.frame.Cluster.name === 'touchlink') {
|
|
485
|
+
// This is handled by touchlink
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
else if (dataPayload.frame.Cluster.name === 'greenPower') {
|
|
489
|
+
await this.greenPower.onZclGreenPowerData(dataPayload);
|
|
490
|
+
// lookup encapsulated gpDevice for further processing
|
|
491
|
+
gpDevice = model_1.Device.byNetworkAddress(dataPayload.frame.Payload.srcID & 0xFFFF, this.database.id);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
let device = gpDevice ? gpDevice : (typeof dataPayload.address === 'string'
|
|
495
|
+
? model_1.Device.byIeeeAddr(this.database.id, dataPayload.address)
|
|
496
|
+
: model_1.Device.byNetworkAddress(dataPayload.address, this.database.id));
|
|
497
|
+
/**
|
|
498
|
+
* Handling of re-transmitted Xiaomi messages.
|
|
499
|
+
* https://github.com/Koenkk/zigbee2mqtt/issues/1238
|
|
500
|
+
* https://github.com/Koenkk/zigbee2mqtt/issues/3592
|
|
501
|
+
*
|
|
502
|
+
* Some Xiaomi router devices re-transmit messages from Xiaomi end devices.
|
|
503
|
+
* The network address of these message is set to the one of the Xiaomi router.
|
|
504
|
+
* Therefore it looks like if the message came from the Xiaomi router, while in
|
|
505
|
+
* fact it came from the end device.
|
|
506
|
+
* Handling these message would result in false state updates.
|
|
507
|
+
* The group ID attribute of these message defines the network address of the end device.
|
|
508
|
+
*/
|
|
509
|
+
if ((device === null || device === void 0 ? void 0 : device.manufacturerName) === 'LUMI' && (device === null || device === void 0 ? void 0 : device.type) == 'Router' && dataPayload.groupID) {
|
|
510
|
+
debug.log(`Handling re-transmitted Xiaomi message ${device.networkAddress} -> ${dataPayload.groupID}`);
|
|
511
|
+
device = model_1.Device.byNetworkAddress(dataPayload.groupID, this.database.id);
|
|
512
|
+
}
|
|
513
|
+
if (!device) {
|
|
514
|
+
debug.log(`'${dataType}' data is from unknown device with address '${dataPayload.address}', ` +
|
|
515
|
+
`skipping...`);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
device.updateLastSeen();
|
|
519
|
+
device.implicitCheckin();
|
|
520
|
+
device.linkquality = dataPayload.linkquality;
|
|
521
|
+
let endpoint = device.getEndpoint(dataPayload.endpoint);
|
|
522
|
+
if (!endpoint) {
|
|
523
|
+
debug.log(`'${dataType}' data is from unknown endpoint '${dataPayload.endpoint}' from device with ` +
|
|
524
|
+
`network address '${dataPayload.address}', creating it...`);
|
|
525
|
+
endpoint = device.createEndpoint(dataPayload.endpoint);
|
|
526
|
+
}
|
|
527
|
+
// Parse command for event
|
|
528
|
+
let type = undefined;
|
|
529
|
+
let data;
|
|
530
|
+
let clusterName = undefined;
|
|
531
|
+
const meta = {};
|
|
532
|
+
if (this.isZclDataPayload(dataPayload, dataType)) {
|
|
533
|
+
const frame = dataPayload.frame;
|
|
534
|
+
const command = frame.getCommand();
|
|
535
|
+
clusterName = frame.Cluster.name;
|
|
536
|
+
meta.zclTransactionSequenceNumber = frame.Header.transactionSequenceNumber;
|
|
537
|
+
meta.manufacturerCode = frame.Header.manufacturerCode;
|
|
538
|
+
meta.frameControl = frame.Header.frameControl;
|
|
539
|
+
if (frame.isGlobal()) {
|
|
540
|
+
if (frame.isCommand('report')) {
|
|
541
|
+
type = 'attributeReport';
|
|
542
|
+
data = helpers_1.ZclFrameConverter.attributeKeyValue(dataPayload.frame);
|
|
543
|
+
}
|
|
544
|
+
else if (frame.isCommand('read')) {
|
|
545
|
+
type = 'read';
|
|
546
|
+
data = helpers_1.ZclFrameConverter.attributeList(dataPayload.frame);
|
|
547
|
+
}
|
|
548
|
+
else if (frame.isCommand('write')) {
|
|
549
|
+
type = 'write';
|
|
550
|
+
data = helpers_1.ZclFrameConverter.attributeKeyValue(dataPayload.frame);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
/* istanbul ignore else */
|
|
554
|
+
if (frame.isCommand('readRsp')) {
|
|
555
|
+
type = 'readResponse';
|
|
556
|
+
data = helpers_1.ZclFrameConverter.attributeKeyValue(dataPayload.frame);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
/* istanbul ignore else */
|
|
562
|
+
if (frame.isSpecific()) {
|
|
563
|
+
if (Events.CommandsLookup[command.name]) {
|
|
564
|
+
type = Events.CommandsLookup[command.name];
|
|
565
|
+
data = dataPayload.frame.Payload;
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
debug.log(`Skipping command '${command.name}' because it is missing from the lookup`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (type === 'readResponse' || type === 'attributeReport') {
|
|
573
|
+
// Some device report, e.g. it's modelID through a readResponse or attributeReport
|
|
574
|
+
for (const [key, value] of Object.entries(data)) {
|
|
575
|
+
const property = model_1.Device.ReportablePropertiesMapping[key];
|
|
576
|
+
if (property && !device[property.key]) {
|
|
577
|
+
property.set(value, device);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
endpoint.saveClusterAttributeKeyValue(frame.Cluster.ID, data);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
type = 'raw';
|
|
585
|
+
data = dataPayload.data;
|
|
586
|
+
const name = zcl_1.Utils.getCluster(dataPayload.clusterID).name;
|
|
587
|
+
clusterName = Number.isNaN(Number(name)) ? name : Number(name);
|
|
588
|
+
}
|
|
589
|
+
if (type && data) {
|
|
590
|
+
const endpoint = device.getEndpoint(dataPayload.endpoint);
|
|
591
|
+
const linkquality = dataPayload.linkquality;
|
|
592
|
+
const groupID = dataPayload.groupID;
|
|
593
|
+
const eventData = {
|
|
594
|
+
type: type, device, endpoint, data, linkquality, groupID, cluster: clusterName, meta
|
|
595
|
+
};
|
|
596
|
+
this.selfAndDeviceEmit(device, Events.Events.message, eventData);
|
|
597
|
+
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'messageEmitted' });
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
this.selfAndDeviceEmit(device, Events.Events.lastSeenChanged, { device, reason: 'messageNonEmitted' });
|
|
601
|
+
}
|
|
602
|
+
if (this.isZclDataPayload(dataPayload, dataType)) {
|
|
603
|
+
device.onZclData(dataPayload, endpoint);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
exports.default = Controller;
|
|
608
608
|
//# sourceMappingURL=controller.js.map
|