@blueid/access-cli 0.21.0 → 0.22.0

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.
@@ -9,7 +9,7 @@ import { BlueSystemProvisioning } from '../proto/BlueSystem_pb.js'
9
9
 
10
10
  export default async (
11
11
  provisioningFile,
12
- { vendorIdentity, hardwareType, hardwareSerialNumber, hardwareVersion, hardwareName, meta, format }
12
+ { vendorIdentity, hardwareType, hardwareSerialNumber, hardwareVersion, hardwareName, meta, format },
13
13
  ) => {
14
14
  const { deviceId, terminalPrivateKey, signaturePublicKey } = await registerDevice({
15
15
  vendorIdentity,
@@ -28,7 +28,11 @@ export default async (
28
28
  signaturePublicKey,
29
29
  })
30
30
 
31
- const buffer = protoDelimited.enc(provisionData)
31
+ const buffer = Buffer.from(protoDelimited.enc(provisionData))
32
32
 
33
- outputBuffer(buffer, format, provisioningFile)
33
+ outputBuffer(buffer, format, provisioningFile, {
34
+ fileHexSize: 512,
35
+ fileHexCrc16: true,
36
+ fileHexCrc16Magic: true,
37
+ })
34
38
  }
@@ -13,36 +13,36 @@ program
13
13
  .addOption(
14
14
  new Option(
15
15
  '--vendorIdentity [vendorIdentity]',
16
- 'The vendor identity to be used for authentication or "test" for test-data'
17
- ).makeOptionMandatory(true)
16
+ 'The vendor identity to be used for authentication or "test" for test-data',
17
+ ).makeOptionMandatory(true),
18
18
  )
19
19
  .addOption(
20
20
  new Option('--hardwareType [hardwareType]', 'The type of the device')
21
21
  .choices(Object.values(BlueHardwareType).filter((x) => typeof x === 'string'))
22
- .makeOptionMandatory(true)
22
+ .makeOptionMandatory(true),
23
23
  )
24
24
  .addOption(
25
25
  new Option(
26
26
  '--hardwareVersion [hardwareVersion]',
27
- 'The actual hardware version of the device which is vendor specific'
28
- )
27
+ 'The actual hardware version of the device which is vendor specific',
28
+ ),
29
29
  )
30
30
  .addOption(
31
31
  new Option(
32
32
  '--hardwareSerialNumber [hardwareSerialNumber]',
33
- 'The vendor serial number for the device which is vendor specific'
34
- ).makeOptionMandatory(true)
33
+ 'The vendor serial number for the device which is vendor specific',
34
+ ).makeOptionMandatory(true),
35
35
  )
36
36
  .addOption(
37
37
  new Option(
38
38
  '--hardwareName [hardwareName]',
39
- 'The human readable identifier of the device which is vendor specific'
40
- ).makeOptionMandatory(true)
39
+ 'The human readable identifier of the device which is vendor specific',
40
+ ).makeOptionMandatory(true),
41
41
  )
42
42
  .addOption(new Option('--meta [meta]', 'Optional meta data in JSON format for the device'))
43
43
  .addOption(
44
44
  new Option('--format [format]', 'The output format to be used')
45
- .choices(['file'].concat(outputFormats))
46
- .makeOptionMandatory()
45
+ .choices(['file', 'file_hex'].concat(outputFormats))
46
+ .makeOptionMandatory(),
47
47
  )
48
48
  .action(action)
@@ -350,7 +350,7 @@ export const BlueLocalTimeSchedule = proto2.makeMessageType(
350
350
  () => [
351
351
  { no: 1, name: "dayOfYearStart", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
352
352
  { no: 2, name: "dayOfYearEnd", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
353
- { no: 3, name: "weekdays", kind: "scalar", T: 12 /* ScalarType.BYTES */ },
353
+ { no: 3, name: "weekdays", kind: "scalar", T: 8 /* ScalarType.BOOL */, repeated: true },
354
354
  { no: 4, name: "timePeriod", kind: "message", T: BlueLocalTimeperiod },
355
355
  ],
356
356
  );
@@ -386,6 +386,7 @@ export const BlueBleManufacturerInfo = proto2.makeMessageType(
386
386
  { no: 2, name: "batteryLevel", kind: "enum", T: proto2.getEnumType(BlueBatteryLevel) },
387
387
  { no: 3, name: "applicationVersion", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
388
388
  { no: 4, name: "localMidnightTimeEpoch", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
389
+ { no: 5, name: "isFactory", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
389
390
  ],
390
391
  );
391
392
 
@@ -439,10 +440,10 @@ export const BlueSPHandshakeReply = proto2.makeMessageType(
439
440
  );
440
441
 
441
442
  /**
442
- * @generated from message BlueSPDataCommand
443
+ * @generated from message BlueSPTokenCommand
443
444
  */
444
- export const BlueSPDataCommand = proto2.makeMessageType(
445
- "BlueSPDataCommand",
445
+ export const BlueSPTokenCommand = proto2.makeMessageType(
446
+ "BlueSPTokenCommand",
446
447
  () => [
447
448
  { no: 1, name: "credentialId", kind: "message", T: BlueCredentialId },
448
449
  { no: 2, name: "validityStart", kind: "message", T: BlueLocalTimestamp },
@@ -453,13 +454,13 @@ export const BlueSPDataCommand = proto2.makeMessageType(
453
454
  );
454
455
 
455
456
  /**
456
- * @generated from message BlueSPData
457
+ * @generated from message BlueSPToken
457
458
  */
458
- export const BlueSPData = proto2.makeMessageType(
459
- "BlueSPData",
459
+ export const BlueSPToken = proto2.makeMessageType(
460
+ "BlueSPToken",
460
461
  () => [
461
462
  { no: 1, name: "signature", kind: "scalar", T: 12 /* ScalarType.BYTES */ },
462
- { no: 2, name: "command", kind: "message", T: BlueSPDataCommand, oneof: "payload" },
463
+ { no: 2, name: "command", kind: "message", T: BlueSPTokenCommand, oneof: "payload" },
463
464
  { no: 3, name: "ossSo", kind: "message", T: BlueOssSoMobile, oneof: "payload" },
464
465
  { no: 4, name: "ossSid", kind: "message", T: BlueOssSidMobile, oneof: "payload" },
465
466
  ],
@@ -695,7 +696,7 @@ export const BlueOssSoCredentialType = proto2.makeMessageType(
695
696
  export const BlueOssSoDTScheduleDay = proto2.makeMessageType(
696
697
  "BlueOssSoDTScheduleDay",
697
698
  () => [
698
- { no: 1, name: "weekdays", kind: "scalar", T: 8 /* ScalarType.BOOL */, repeated: true },
699
+ { no: 3, name: "weekdays", kind: "scalar", T: 8 /* ScalarType.BOOL */, repeated: true },
699
700
  { no: 8, name: "timePeriods", kind: "message", T: BlueLocalTimeperiod, repeated: true },
700
701
  ],
701
702
  );
@@ -7,38 +7,16 @@ import { proto2 } from "@bufbuild/protobuf";
7
7
  import { BlueBatteryLevel, BlueBlacklistEntry, BlueEvent, BlueHardwareType, BlueLocalTimeSchedule, BlueLocalTimestamp } from "./BlueCore_pb.js";
8
8
  import { BlueLockConfig, BlueLockStatus } from "./BlueLock_pb.js";
9
9
 
10
- /**
11
- * @generated from enum BlueOnlineMode
12
- */
13
- export const BlueOnlineMode = proto2.makeEnum(
14
- "BlueOnlineMode",
15
- [
16
- {no: 0, name: "OfflineMode"},
17
- {no: 1, name: "ManualWifi"},
18
- {no: 2, name: "AlwaysOnWifi"},
19
- {no: 3, name: "UseLan"},
20
- ],
21
- );
22
-
23
- /**
24
- * @generated from message BlueTimeOffsetPeriod
25
- */
26
- export const BlueTimeOffsetPeriod = proto2.makeMessageType(
27
- "BlueTimeOffsetPeriod",
28
- () => [
29
- { no: 1, name: "epoch", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
30
- { no: 2, name: "offset", kind: "scalar", T: 5 /* ScalarType.INT32 */ },
31
- ],
32
- );
33
-
34
10
  /**
35
11
  * @generated from message BlueBaseConfig
36
12
  */
37
13
  export const BlueBaseConfig = proto2.makeMessageType(
38
14
  "BlueBaseConfig",
39
15
  () => [
40
- { no: 1, name: "timeOffsetPeriods", kind: "message", T: BlueTimeOffsetPeriod, repeated: true },
41
- { no: 2, name: "autoCheckSystemStatus", kind: "scalar", T: 8 /* ScalarType.BOOL */, default: false },
16
+ { no: 1, name: "isoCountry", kind: "scalar", T: 9 /* ScalarType.STRING */ },
17
+ { no: 2, name: "isoState", kind: "scalar", T: 9 /* ScalarType.STRING */ },
18
+ { no: 3, name: "utcOffsetMinutes", kind: "scalar", T: 5 /* ScalarType.INT32 */, default: 0 },
19
+ { no: 4, name: "autoCheckSystemStatus", kind: "scalar", T: 8 /* ScalarType.BOOL */, default: false },
42
20
  ],
43
21
  );
44
22
 
@@ -65,11 +43,21 @@ export const BlueBleConfig = proto2.makeMessageType(
65
43
  export const BlueOnlineConfig = proto2.makeMessageType(
66
44
  "BlueOnlineConfig",
67
45
  () => [
68
- { no: 1, name: "mode", kind: "enum", T: proto2.getEnumType(BlueOnlineMode), default: BlueOnlineMode.OfflineMode },
69
- { no: 2, name: "connectSchedules", kind: "message", T: BlueLocalTimeSchedule, repeated: true },
70
- { no: 3, name: "wifiSSID", kind: "scalar", T: 9 /* ScalarType.STRING */ },
71
- { no: 4, name: "wifiPassword", kind: "scalar", T: 9 /* ScalarType.STRING */ },
72
- { no: 5, name: "timeoutSec", kind: "scalar", T: 13 /* ScalarType.UINT32 */, default: 30 },
46
+ { no: 1, name: "connectSchedules", kind: "message", T: BlueLocalTimeSchedule, repeated: true },
47
+ { no: 2, name: "wifiSSID", kind: "scalar", T: 9 /* ScalarType.STRING */ },
48
+ { no: 3, name: "wifiPassword", kind: "scalar", T: 9 /* ScalarType.STRING */ },
49
+ { no: 4, name: "timeoutSec", kind: "scalar", T: 13 /* ScalarType.UINT32 */, default: 30 },
50
+ ],
51
+ );
52
+
53
+ /**
54
+ * @generated from message BlueAccessConfigGroup
55
+ */
56
+ export const BlueAccessConfigGroup = proto2.makeMessageType(
57
+ "BlueAccessConfigGroup",
58
+ () => [
59
+ { no: 1, name: "groupId", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
60
+ { no: 2, name: "schedules", kind: "message", T: BlueLocalTimeSchedule, repeated: true },
73
61
  ],
74
62
  );
75
63
 
@@ -81,7 +69,7 @@ export const BlueAccessConfig = proto2.makeMessageType(
81
69
  () => [
82
70
  { no: 1, name: "id", kind: "scalar", T: 13 /* ScalarType.UINT32 */, default: 1 },
83
71
  { no: 2, name: "siteId", kind: "scalar", T: 13 /* ScalarType.UINT32 */, default: 1 },
84
- { no: 3, name: "groupIds", kind: "scalar", T: 13 /* ScalarType.UINT32 */, repeated: true },
72
+ { no: 3, name: "groups", kind: "message", T: BlueAccessConfigGroup, repeated: true },
85
73
  ],
86
74
  );
87
75
 
@@ -112,7 +100,7 @@ export const BlueOssConfig = proto2.makeMessageType(
112
100
  export const BlueSystemConfig = proto2.makeMessageType(
113
101
  "BlueSystemConfig",
114
102
  () => [
115
- { no: 1, name: "version", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
103
+ { no: 1, name: "configId", kind: "scalar", T: 9 /* ScalarType.STRING */ },
116
104
  { no: 2, name: "base", kind: "message", T: BlueBaseConfig },
117
105
  { no: 3, name: "ble", kind: "message", T: BlueBleConfig },
118
106
  { no: 4, name: "online", kind: "message", T: BlueOnlineConfig },
@@ -184,7 +172,7 @@ export const BlueSystemProvisioning = proto2.makeMessageType(
184
172
  export const BlueSystemStatus = proto2.makeMessageType(
185
173
  "BlueSystemStatus",
186
174
  () => [
187
- { no: 1, name: "configVersion", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
175
+ { no: 1, name: "configId", kind: "scalar", T: 9 /* ScalarType.STRING */ },
188
176
  { no: 2, name: "deviceId", kind: "scalar", T: 9 /* ScalarType.STRING */ },
189
177
  { no: 3, name: "serialNumber", kind: "scalar", T: 9 /* ScalarType.STRING */ },
190
178
  { no: 4, name: "hardwareType", kind: "enum", T: proto2.getEnumType(BlueHardwareType) },
@@ -0,0 +1,246 @@
1
+ // From https://en.wikipedia.org/wiki/Intel_HEX
2
+ //
3
+ // Intel Hex Line Format (I32HEX, 32 bit addresses) is
4
+ // :llaaaatt[dd...]cc
5
+ // Where
6
+ // ll=data length
7
+ // aaaa=lower 16 bit address
8
+ // tt=record type
9
+ // 00 - data record
10
+ // 01 - end-of-file record
11
+ // 02 - extended segment address record (Not used in I32Hex)
12
+ // 04 - extended linear address record
13
+ // 05 - start linear address record (MDK-ARM only)
14
+ // cc=checksum = 2's comp of negative of sum of all record bytes
15
+
16
+ var DEFAULT_LINE_TERMINATOR = '\n'
17
+ var SEGMENT_LENGTH_BYTES = 0x10000
18
+ var MAX_BASE_ADDRESS = 0xffff
19
+
20
+ const utils = {
21
+ numberToHexString: function (d, padding) {
22
+ var hex = Number(d).toString(16).toUpperCase()
23
+ padding = typeof padding === 'undefined' || padding === null ? (padding = 8) : padding
24
+
25
+ while (hex.length < padding) {
26
+ hex = '0' + hex
27
+ }
28
+
29
+ return hex
30
+ },
31
+ numberToHexStringLittleEndian: function (d, padding) {
32
+ if (padding % 2 != 0)
33
+ throw new Error('numberToHexStringLittleEndian requires an even padding [' + padding + ']')
34
+
35
+ var bigEndianHex = utils.numberToHexString(d, padding)
36
+
37
+ var littleEndianHex = ''
38
+ while (bigEndianHex.length) {
39
+ var nibble = bigEndianHex[0] + bigEndianHex[1]
40
+ bigEndianHex = bigEndianHex.slice(2)
41
+ littleEndianHex = nibble + littleEndianHex
42
+ }
43
+
44
+ return littleEndianHex
45
+ },
46
+ hexStringByWords: function (hex, littleEndian) {
47
+ if (!hex || hex.length % 8 !== 0) throw new Error('hexStringByWords requires a word-aligned hex string')
48
+ littleEndian = !!littleEndian
49
+
50
+ hex = hex.toUpperCase()
51
+ const wordStrings = []
52
+
53
+ for (var i = 0; i < hex.length; i += 8) {
54
+ var ws = hex.substring(i, i + 8)
55
+ if (littleEndian) {
56
+ ws = ws.substring(6, 8) + ws.substring(4, 6) + ws.substring(2, 4) + ws.substring(0, 2)
57
+ }
58
+ wordStrings.push(ws)
59
+ }
60
+
61
+ return wordStrings.join(' ')
62
+ },
63
+ hexToDecimal: function (hexString) {
64
+ try {
65
+ return parseInt(hexString, 16)
66
+ } catch (e) {
67
+ console.error('Error parsing hexString: ', e)
68
+ return 0
69
+ }
70
+ },
71
+ toUInt32: function (val) {
72
+ return val >>> 0
73
+ },
74
+ }
75
+
76
+ function IntelHexFileOut(startAddress) {
77
+ if (startAddress === void 0) startAddress = 0
78
+
79
+ this.contents = ''
80
+ this.extendedAddress = 0
81
+ this.relativeAddress = 0
82
+ this.lastAppendEndedAtBoundary = false
83
+ this.lineTerminator = DEFAULT_LINE_TERMINATOR
84
+
85
+ this.setAddress(startAddress)
86
+ }
87
+
88
+ IntelHexFileOut.withIntelHex = function (initialHexString, startAddress) {
89
+ const intelhexProcessor = new IntelHexFileOut(startAddress)
90
+ intelhexProcessor.contents = initialHexString.toUpperCase()
91
+ intelhexProcessor.reopen()
92
+ return intelhexProcessor
93
+ }
94
+
95
+ IntelHexFileOut.prototype.reopen = function () {
96
+ const indexOfTerminator = this.contents.indexOf(this.eofMarker())
97
+ if (indexOfTerminator !== -1) {
98
+ this.contents = this.contents.substring(0, indexOfTerminator)
99
+ }
100
+ }
101
+
102
+ IntelHexFileOut.prototype.eofMarker = function () {
103
+ return ':00000001FF' + this.lineTerminator
104
+ }
105
+
106
+ IntelHexFileOut.prototype.setAddress = function (address) {
107
+ var extendedAddress = this._extendedAddressFromAddress(address)
108
+ this.relativeAddress = this._addressRelativeToExtendedAddress(address, extendedAddress)
109
+
110
+ if (this.extendedAddress === extendedAddress) return
111
+ this.extendedAddress = extendedAddress
112
+
113
+ this._writeExtendedAddress()
114
+
115
+ this.lastAppendEndedAtBoundary = false
116
+ }
117
+
118
+ IntelHexFileOut.prototype.writeHexStringAtAddress = function (address, hexString) {
119
+ if (!hexString || hexString.length === 0 || hexString.length > SEGMENT_LENGTH_BYTES || hexString.length % 2 !== 0) {
120
+ throw new Error('invalid length of hexString [' + hexString.length + ']')
121
+ }
122
+
123
+ this.setAddress(address)
124
+ this.appendHexString(hexString)
125
+ }
126
+
127
+ IntelHexFileOut.prototype.appendBuffer = function (buffer, littleEndian) {
128
+ if (!buffer) throw new Error('Buffer required')
129
+ if (buffer.length % 4 !== 0) throw new Error('Buffer length not 32 bit word aligned')
130
+
131
+ const words = []
132
+ for (var offset = 0; offset < buffer.length; offset += 4) {
133
+ const uint32 = littleEndian ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset)
134
+ words.push(uint32)
135
+ }
136
+
137
+ this.appendWords(words, littleEndian)
138
+ }
139
+
140
+ IntelHexFileOut.prototype.appendWords = function (words, littleEndian) {
141
+ if (!words || words.length === 0) {
142
+ // || words.length * 4 > SEGMENT_LENGTH_BYTES ){
143
+ throw new Error('invalid number of words [' + words.length + ']')
144
+ }
145
+
146
+ var encodeAsString = littleEndian ? utils.numberToHexStringLittleEndian : utils.numberToHexString
147
+
148
+ var lineHexString = ''
149
+ while (words.length) {
150
+ lineHexString += encodeAsString(words.shift(), 8)
151
+ if (lineHexString.length === 32) {
152
+ this.appendHexString(lineHexString)
153
+ lineHexString = ''
154
+ }
155
+ }
156
+
157
+ if (lineHexString.length) {
158
+ this.appendHexString(lineHexString)
159
+ }
160
+ }
161
+
162
+ IntelHexFileOut.prototype.appendHexString = function (hexString) {
163
+ if (!hexString || hexString.length === 0 || hexString.length > SEGMENT_LENGTH_BYTES || hexString.length % 2 !== 0) {
164
+ throw new Error('invalid length of hexString [' + hexString.length + ']')
165
+ }
166
+
167
+ while (hexString.length) {
168
+ var lineHexCharLength = hexString.length < 32 ? hexString.length : 32
169
+ var lineHexString = hexString.slice(0, lineHexCharLength)
170
+
171
+ hexString = hexString.slice(lineHexCharLength)
172
+
173
+ this._handleBlockContinuation()
174
+
175
+ var byteCount = lineHexString.length / 2
176
+
177
+ if (this.relativeAddress + byteCount > 0x10000) {
178
+ throw new Error('appendHexString cannot write across 63336 byte boundary')
179
+ }
180
+
181
+ this._writeHexString(lineHexString)
182
+
183
+ this.relativeAddress += byteCount
184
+ if (this.relativeAddress == SEGMENT_LENGTH_BYTES) {
185
+ this.extendedAddress += 0x0001
186
+ this.relativeAddress = 0x0000
187
+ this.lastAppendEndedAtBoundary = true
188
+ }
189
+ }
190
+ }
191
+
192
+ IntelHexFileOut.prototype.close = function () {
193
+ this.contents += this.eofMarker()
194
+ }
195
+
196
+ IntelHexFileOut.prototype._writeExtendedAddress = function () {
197
+ var data = this.extendedAddress
198
+ var intelHex = '02' + '0000' + '04' + utils.numberToHexString(data, 4)
199
+ intelHex += this._crc(intelHex)
200
+ this.contents += ':' + intelHex + this.lineTerminator
201
+ }
202
+
203
+ IntelHexFileOut.prototype._writeHexString = function (hexString) {
204
+ var byteCount = hexString.length / 2
205
+ var intelHex =
206
+ utils.numberToHexString(byteCount, 2) + utils.numberToHexString(this.relativeAddress, 4) + '00' + hexString
207
+ intelHex += this._crc(intelHex)
208
+ this.contents += ':' + intelHex + this.lineTerminator
209
+ }
210
+
211
+ IntelHexFileOut.prototype._handleBlockContinuation = function () {
212
+ if (this.lastAppendEndedAtBoundary) {
213
+ this._writeExtendedAddress()
214
+ this.lastAppendEndedAtBoundary = false
215
+ }
216
+ }
217
+
218
+ IntelHexFileOut.prototype._crc = function (intelHexString) {
219
+ var sum = 0
220
+ for (var i = 0; i < intelHexString.length; i += 2) {
221
+ var byteHexString = intelHexString.charAt(i) + intelHexString.charAt(i + 1)
222
+ sum += utils.hexToDecimal(byteHexString) & 0xff
223
+ }
224
+ var crc = ((sum ^ 0xff) + 1) & 0xff
225
+ return utils.numberToHexString(crc, 2)
226
+ }
227
+
228
+ IntelHexFileOut.prototype._extendedAddressFromAddress = function (address) {
229
+ return address >>> 16
230
+ }
231
+
232
+ IntelHexFileOut.prototype._addressRelativeToExtendedAddress = function (address, extendedAddress) {
233
+ var baseAddress = utils.toUInt32(extendedAddress << 16)
234
+ if (address < baseAddress || address - baseAddress > MAX_BASE_ADDRESS) {
235
+ throw new Error('address must be 0 to 65535 bytes above baseAddress [' + baseAddress + ']')
236
+ }
237
+
238
+ return address - baseAddress
239
+ }
240
+
241
+ export default (startAddress, littleEndian, buffer) => {
242
+ var processor = new IntelHexFileOut(startAddress)
243
+ processor.appendBuffer(buffer, littleEndian)
244
+ processor.close()
245
+ return processor.contents
246
+ }
@@ -0,0 +1,26 @@
1
+ const STORAGE_CRC_16_MAGIC_NUMBER = 0x92db
2
+
3
+ export default (buffer, start, magicMarker = false) => {
4
+ // CRC-16-CCITT (XModem)
5
+ const crcTable = [
6
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad,
7
+ 0xe1ce, 0xf1ef,
8
+ ]
9
+
10
+ let crc = 0xffff
11
+
12
+ const data = new Uint8Array(buffer)
13
+
14
+ for (let i = start; i < data.length; i++) {
15
+ crc = (crc << 4) ^ crcTable[((crc >>> 12) ^ data[i]) & 0x0f]
16
+ crc = (crc << 4) ^ crcTable[((crc >>> 12) ^ (data[i] >>> 4)) & 0x0f]
17
+ }
18
+
19
+ if (magicMarker) {
20
+ crc ^= STORAGE_CRC_16_MAGIC_NUMBER
21
+ }
22
+
23
+ crc &= 0xffff
24
+
25
+ return crc
26
+ }
@@ -1,6 +1,9 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs'
3
3
 
4
+ import binaryToIntelHex from './binaryToIntelHex.js'
5
+ import crc16 from './crc16.js'
6
+
4
7
  const bufferToCArray = (buffer) => {
5
8
  const hexString = buffer.toString('hex')
6
9
  const hexArray = hexString.match(/.{1,2}/g)
@@ -13,9 +16,41 @@ const bufferToProtoArray = (buffer) => {
13
16
  return hexArray.map((hex) => `\\x${hex}`).join('')
14
17
  }
15
18
 
19
+ const bufferToIntelHex = (buffer, hexStartAddress, hexSize, hexCrc16, hexCrc16Magic) => {
20
+ if (typeof hexStartAddress !== 'number') {
21
+ throw new Error('Invalid hexStartAddress')
22
+ }
23
+
24
+ if (typeof hexSize !== 'number') {
25
+ throw new Error('Invalid hexSize')
26
+ }
27
+
28
+ if (typeof hexCrc16 !== 'boolean') {
29
+ throw new Error('Invalid hexCrc16')
30
+ }
31
+
32
+ if (typeof hexCrc16Magic !== 'boolean') {
33
+ throw new Error('Invalid hexCrc16Magic')
34
+ }
35
+
36
+ const binaryBuffer = Buffer.alloc(hexSize)
37
+
38
+ if (hexCrc16) {
39
+ buffer.copy(binaryBuffer, 2, 0)
40
+ const crc16_ = crc16(binaryBuffer, 2, hexCrc16Magic)
41
+ binaryBuffer.writeUInt16LE(crc16_, 0)
42
+ console.log(`Intel Hex, startAddress=${hexStartAddress}, size=${hexSize}, crc16=${crc16_}`)
43
+ } else {
44
+ buffer.copy(binaryBuffer, 0, 0)
45
+ console.log(`Intel Hex, startAddress=${hexStartAddress}, size=${hexSize}`)
46
+ }
47
+
48
+ return binaryToIntelHex(hexStartAddress, true, binaryBuffer)
49
+ }
50
+
16
51
  export const outputFormats = ['hex', 'cpphex', 'protohex', 'base64']
17
52
 
18
- export default (buffer, format, filename) => {
53
+ export default (buffer, format, filename, settings = {}) => {
19
54
  let result = null
20
55
 
21
56
  switch (format) {
@@ -31,13 +66,34 @@ export default (buffer, format, filename) => {
31
66
  case 'base64':
32
67
  result = buffer.toString('base64')
33
68
  break
34
- default:
69
+ case 'file':
35
70
  {
36
71
  const filepath = path.resolve(filename)
37
72
  fs.writeFileSync(filepath, buffer)
38
73
  result = `Written to file <${filepath}>`
39
74
  }
40
75
  break
76
+ case 'file_hex':
77
+ {
78
+ const [startAddress, filename_] = filename.split(':')
79
+
80
+ const startAddressNo = Number.parseInt(startAddress)
81
+ if (Number.isNaN(startAddressNo)) {
82
+ throw new Error(`Invalid startAddress ${startAddress}`)
83
+ }
84
+
85
+ const { fileHexSize, fileHexCrc16, fileHexCrc16Magic } = settings
86
+
87
+ const hexData = bufferToIntelHex(buffer, startAddressNo, fileHexSize, fileHexCrc16, fileHexCrc16Magic)
88
+
89
+ const filepath = path.resolve(filename_)
90
+ fs.writeFileSync(filepath, hexData)
91
+
92
+ result = `Written to file <${filepath}>`
93
+ }
94
+ break
95
+ default:
96
+ throw new Error(`Unknown format ${format}`)
41
97
  }
42
98
 
43
99
  console.log(`(${format} with ${buffer.length.toString()} bytes)\n`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueid/access-cli",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "BlueID Access CLI",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -14,7 +14,7 @@
14
14
  "package.json"
15
15
  ],
16
16
  "scripts": {
17
- "sync-proto": "sh syncproto.sh",
17
+ "sync-proto": "sh syncproto.sh || exit 0",
18
18
  "preinstall": "npm run sync-proto",
19
19
  "prepack": "npm run sync-proto"
20
20
  },