diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index ff35ca1..3ca577c 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -13,8 +13,10 @@ module.exports = {
},
rules: {
'@typescript-eslint/no-unsafe-argument': 'off',
+ '@typescript-eslint/non-nullable-type-assertion-style': 'off',
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/strict-boolean-expressions': 'off',
+ '@typescript-eslint/unbound-method': 'off',
'multiline-ternary': 'off',
'no-extra-boolean-cast': 'off',
'no-return-await': 'off',
@@ -39,8 +41,5 @@ module.exports = {
tuples: 'always-multiline',
functions: 'only-multiline',
}],
- '@typescript-eslint/unbound-method': ['error', {
- ignoreStatic: true,
- }]
},
}
diff --git a/.npmignore b/.npmignore
index 9a8c8c3..3e9d8be 100644
--- a/.npmignore
+++ b/.npmignore
@@ -157,4 +157,5 @@ out
/tsconfig.*.json
/tsconfig.json
/tsdoc.json
+/typedoc
/typedoc.json
diff --git a/README.md b/README.md
index 9b8ca21..bd41cb9 100644
--- a/README.md
+++ b/README.md
@@ -178,3 +178,8 @@ await run(ultraUsb)
- [GitHub RfidResearchGroup/ChameleonUltra](https://github.com/RfidResearchGroup/ChameleonUltra)
- [Chameleon Ultra Guide](https://chameleonultra.com/docs)
- [Chameleon Ultra GUI Documentation](https://docs.chameleonultragui.dev/)
+
+## Dependents (projects / website using chameleon-ultra.js)
+
+- Sil's Website: `https://drosi.nl/cu` (link is broken!)
+- [Tech Security Tools's Website](https://chameleon-ultra.com/)
diff --git a/package.json b/package.json
index b11acda..424b3b7 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"module": "./dist/index.mjs",
"name": "chameleon-ultra.js",
"type": "commonjs",
- "version": "0.3.7",
+ "version": "0.3.8",
"bugs": {
"url": "https://github.com/taichunmin/chameleon-ultra.js/issues"
},
@@ -20,7 +20,7 @@
}
],
"dependencies": {
- "@taichunmin/buffer": "^0.13.4",
+ "@taichunmin/buffer": "^0.13.6",
"debug": "^4.3.4",
"lodash": "^4.17.21",
"serialport": "^12.0.0",
@@ -67,6 +67,7 @@
"serve-static": "^1.15.0",
"supports-color": "^9.4.0",
"ts-jest": "^29.1.4",
+ "ts-node": "^10.9.2",
"tsup": "^8.0.2",
"tsx": "^4.11.0",
"typedoc": "^0.25.13",
@@ -159,7 +160,7 @@
"dev:https": "tsx ./https.ts",
"dev:js": "yarn build:js --watch",
"dev:pug": "nodemon --watch pug --ext pug --exec \"yarn build:pug\"",
- "dev": "DEBUG_COLORS=1 concurrently --names \"DOC,HTTPS,PUBLIC,PUG,TS\" -c \"bgGray,bgGreen,bgGray,bgBlue,bgYellow\" \"yarn dev:docs\" \"yarn dev:https\" \"yarn dev:public\" \"yarn dev:pug\" \"yarn dev:js\"",
+ "dev": "DEBUG_COLORS=1 concurrently --names \"DOC,HTTPS,PUG,TS\" -c \"bgGray,bgGreen,bgGray,bgBlue,bgYellow\" \"yarn dev:docs\" \"yarn dev:https\" \"yarn dev:pug\" \"yarn dev:js\"",
"lint:ci": "eslint --ext .mjs,.js,.js,.ts,.pug .",
"lint": "yarn lint:ci --fix",
"mkcert": "mkdir ./mkcert && mkcert -key-file ./mkcert/key.pem -cert-file ./mkcert/cert.pem -ecdsa localhost",
diff --git a/pug/include/bootstrapV4.pug b/pug/include/bootstrapV4.pug
index dd4afb8..010eab1 100644
--- a/pug/include/bootstrapV4.pug
+++ b/pug/include/bootstrapV4.pug
@@ -57,6 +57,7 @@ html(lang="zh-Hant")
//- pn532 require lodash
script(crossorigin="anonymous", src=`${baseurl}index.global.js`)
script(crossorigin="anonymous", src=`${baseurl}Crypto1.global.js`)
+ script(crossorigin="anonymous", src=`${baseurl}plugin/Debug.global.js`)
script(crossorigin="anonymous", src=`${baseurl}plugin/WebbleAdapter.global.js`)
script(crossorigin="anonymous", src=`${baseurl}plugin/WebserialAdapter.global.js`)
block script
diff --git a/pug/src/device-settings.pug b/pug/src/device-settings.pug
index 32b0ed7..a0ad8bc 100644
--- a/pug/src/device-settings.pug
+++ b/pug/src/device-settings.pug
@@ -106,10 +106,12 @@ block content
block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/joi@17/dist/joi-browser.min.js")
script.
- const { AnimationMode, ButtonAction, ButtonType, ChameleonUltra, DeviceMode, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
+ const { AnimationMode, ButtonAction, ButtonType, ChameleonDebug, ChameleonUltra, DeviceMode, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
const ultraUsb = new ChameleonUltra(true)
+ ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra(true)
+ ultraBle.use(new ChameleonDebug())
ultraBle.use(new WebbleAdapter())
const { joi: Joi } = window
diff --git a/pug/src/mfkey32.pug b/pug/src/mfkey32.pug
index 1117a3c..ab7d620 100644
--- a/pug/src/mfkey32.pug
+++ b/pug/src/mfkey32.pug
@@ -108,10 +108,12 @@ block content
block script
script.
- const { Buffer, ChameleonUltra, DeviceMode, FreqType, Mf1KeyType, TagType, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
+ const { Buffer, ChameleonDebug, ChameleonUltra, DeviceMode, FreqType, Mf1KeyType, TagType, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
const ultraUsb = new ChameleonUltra(true)
+ ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra(true)
+ ultraBle.use(new ChameleonDebug())
ultraBle.use(new WebbleAdapter())
window.vm = new Vue({
diff --git a/pug/src/mifare-value.pug b/pug/src/mifare-value.pug
index 606190d..d808c3d 100644
--- a/pug/src/mifare-value.pug
+++ b/pug/src/mifare-value.pug
@@ -93,10 +93,12 @@ block content
block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/joi@17/dist/joi-browser.min.js")
script.
- const { Buffer, ChameleonUltra, DeviceMode, Mf1KeyType, Mf1VblockOperator, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
+ const { Buffer, ChameleonDebug, ChameleonUltra, DeviceMode, Mf1KeyType, Mf1VblockOperator, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
const ultraUsb = new ChameleonUltra(true)
+ ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra(true)
+ ultraBle.use(new ChameleonDebug())
ultraBle.use(new WebbleAdapter())
window.vm = new Vue({
diff --git a/pug/src/mifare1k.pug b/pug/src/mifare1k.pug
index 61f2d54..0c41437 100644
--- a/pug/src/mifare1k.pug
+++ b/pug/src/mifare1k.pug
@@ -161,10 +161,12 @@ block content
block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/joi@17/dist/joi-browser.min.js")
script.
- const { AnimationMode, Buffer, ButtonAction, ChameleonUltra, DeviceMode, FreqType, TagType, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
+ const { AnimationMode, Buffer, ButtonAction, ChameleonDebug, ChameleonUltra, DeviceMode, FreqType, TagType, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
const ultraUsb = new ChameleonUltra(true)
+ ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra(true)
+ ultraBle.use(new ChameleonDebug())
ultraBle.use(new WebbleAdapter())
const WELL_KNOWN_KEYS = ['ffffffffffff', 'a0a1a2a3a4a5', 'd3f7d3f7d3f7']
@@ -275,14 +277,14 @@ block script
await ultra.cmdMf1SetGen2Mode(this.ss.gen2)
await ultra.cmdMf1SetWriteMode(this.ss.write)
await ultra.cmdHf14aSetAntiCollData({
- atqa: Buffer.fromHexString(this.ss.atqa).reverse(),
- ats: Buffer.fromHexString(this.ss.ats),
- sak: Buffer.fromHexString(this.ss.sak),
- uid: Buffer.fromHexString(this.ss.uid),
+ atqa: Buffer.from(this.ss.atqa, 'hex').reverse(),
+ ats: Buffer.from(this.ss.ats, 'hex'),
+ sak: Buffer.from(this.ss.sak, 'hex'),
+ uid: Buffer.from(this.ss.uid, 'hex'),
})
for (let i = 0; i < 16; i++) {
if (!this.ss.toggle[i]) continue
- const sectorData = Buffer.fromHexString(this.ss.body[i])
+ const sectorData = Buffer.from(this.ss.body[i], 'hex')
await ultra.cmdMf1EmuWriteBlock(i << 2, sectorData)
}
await Swal.fire({ icon: 'success', title: 'Emulate success' })
@@ -339,7 +341,7 @@ block script
const ultra = this.ultra
for (let i = 0; i < 16; i++) {
if (!this.ss.toggle[i]) continue
- const sectorData = Buffer.fromHexString(this.ss.body[i])
+ const sectorData = Buffer.from(this.ss.body[i], 'hex')
await ultra.mf1Gen1aWriteBlocks(i << 2, sectorData)
this.showLoading(genSwalCfg(i + 1))
}
@@ -399,7 +401,7 @@ block script
for (let i = 0; i < 16; i++) {
try {
if (!this.ss.toggle[i]) continue
- const sectorData = Buffer.fromHexString(this.ss.body[i])
+ const sectorData = Buffer.from(this.ss.body[i], 'hex')
const { success } = await ultra.mf1WriteSectorByKeys(i, keys, sectorData)
for (let j = 0; j < 4; j++) if (!success[j]) failed.push(i * 4 + j)
} catch (err) {
@@ -424,7 +426,7 @@ block script
let keys = []
for (let i = 0; i < 16; i++) {
if (!this.ss.toggle[i]) continue
- const sectorData = Buffer.fromHexString(this.ss.body[i])
+ const sectorData = Buffer.from(this.ss.body[i], 'hex')
if (sectorData.length !== 64) continue
keys.push(..._.map([48, 58], offset => sectorData.subarray(offset, offset + 6).toString('hex')))
}
@@ -490,7 +492,7 @@ block script
const json = JSON5.parse(buf.toString('utf8'))
if (json.FileType !== 'mfcard') throw new Error(`Invalid file type: ${json.FileType}`)
if (!_.isNil(json?.Card?.UID)) this.$set(this.ss, 'uid', _.toLower(json.Card.UID))
- if (!_.isNil(json?.Card?.ATQA)) this.$set(this.ss, 'atqa', Buffer.fromHexString(json.Card.ATQA).reverse().toString('hex'))
+ if (!_.isNil(json?.Card?.ATQA)) this.$set(this.ss, 'atqa', Buffer.from(json.Card.ATQA, 'hex').reverse().toString('hex'))
if (!_.isNil(json?.Card?.SAK)) this.$set(this.ss, 'sak', json.Card.SAK)
if (!_.isNil(json?.blocks)) {
for (let i = 0; i < 16; i++) {
@@ -498,7 +500,7 @@ block script
for (let j = 0; j < 4; j++) {
const blockhex = json?.blocks?.[i * 4 + j] ?? ''
if (blockhex.length !== 32) continue
- const blockbuf = Buffer.fromHexString(blockhex.replaceAll('-', '0'))
+ const blockbuf = Buffer.from(blockhex.replaceAll('-', '0'), 'hex')
if (blockbuf.length !== 16) continue
blockbuf.copy(sectorData, j * 16)
}
@@ -507,7 +509,7 @@ block script
}
},
async btnCardImportEml (file, buf) {
- buf = Buffer.fromHexString(buf.toString('utf8').replaceAll('-', '0'))
+ buf = Buffer.from(buf.toString('utf8').replaceAll('-', '0'), 'hex')
if (buf.length !== 1024) throw new Error(`Invalid eml size: ${buf.length} bytes`)
for (let i = 0; i < 16; i++) {
const sectorData = buf.subarray(i * 64, (i + 1) * 64)
@@ -523,7 +525,7 @@ block script
blockNo = _.parseInt(row.slice(9)) * 4
} else if (/^[0-9a-fA-F-]{32}$/.test(row)) { // hex
if (blockNo >= 64) throw new Error(`Invalid block number: ${blockNo}`)
- const blockbuf = Buffer.fromHexString(row.replaceAll('-', '0'))
+ const blockbuf = Buffer.from(row.replaceAll('-', '0'), 'hex')
if (blockbuf.length !== 16) throw new Error(`Invalid block size: ${blockbuf.length} bytes`)
blockbuf.copy(buf, blockNo * 16)
blockNo++
@@ -534,13 +536,13 @@ block script
async btnCardExport () {
const card = new Buffer(1024)
for (let i = 0; i < 16; i++) {
- const sectorData = Buffer.fromHexString(this.ss.body[i])
+ const sectorData = Buffer.from(this.ss.body[i], 'hex')
if (sectorData.length !== 64) continue // skip invalid sector
sectorData.copy(card, i * 64)
}
- const uid = Buffer.fromHexString(this.ss.uid)
- const atqa = Buffer.fromHexString(this.ss.atqa).reverse()
- const sak = Buffer.fromHexString(this.ss.sak)
+ const uid = Buffer.from(this.ss.uid, 'hex')
+ const atqa = Buffer.from(this.ss.atqa, 'hex').reverse()
+ const sak = Buffer.from(this.ss.sak, 'hex')
// helper
const toHex = buf => _.toUpper(buf.toString('hex'))
diff --git a/pug/src/test.pug b/pug/src/test.pug
index 23733bf..7f7f603 100644
--- a/pug/src/test.pug
+++ b/pug/src/test.pug
@@ -24,11 +24,12 @@ block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/vconsole@3/dist/vconsole.min.js")
script.
window.vConsole = new window.VConsole()
- const { ChameleonUltra, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
- window.Buffer = ChameleonUltraJS.Buffer
+ const { Buffer, ChameleonDebug, ChameleonUltra, Debug, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
const ultraUsb = new ChameleonUltra(true)
+ ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra(true)
+ ultraBle.use(new ChameleonDebug())
ultraBle.use(new WebbleAdapter())
window.vm = new Vue({
diff --git a/src/ChameleonUltra.test.ts b/src/ChameleonUltra.test.ts
index 8a619f6..c468c80 100644
--- a/src/ChameleonUltra.test.ts
+++ b/src/ChameleonUltra.test.ts
@@ -25,87 +25,88 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
beforeEach(async () => {
ultra = new ChameleonUltra()
adapter = new BufferMockAdapter()
+ ultra.readDefaultTimeout = 100 // 100ms
await ultra.use(adapter)
})
test('#cmdBleDeleteAllBonds()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0408 0068 0000 8c 00'))
+ adapter.send.push(Buffer.from('11ef 0408 0068 0000 8c 00', 'hex'))
// act
await ultra.cmdBleDeleteAllBonds()
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0408 0000 0000 f4 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0408 0000 0000 f4 00', 'hex')])
})
test('#cmdBleGetAddress()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f4 0068 0006 9b d9cbc1d2b25f b8'))
+ adapter.send.push(Buffer.from('11ef 03f4 0068 0006 9b d9cbc1d2b25f b8', 'hex'))
// act
const actual = await ultra.cmdBleGetAddress()
// assert
expect(actual).toEqual('D9:CB:C1:D2:B2:5F')
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f4 0000 0000 09 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f4 0000 0000 09 00', 'hex')])
})
test('#cmdBleGetPairingKey()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0407 0068 0006 87 313233343536 cb'))
+ adapter.send.push(Buffer.from('11ef 0407 0068 0006 87 313233343536 cb', 'hex'))
// act
const actual = await ultra.cmdBleGetPairingKey()
// assert
expect(actual).toEqual('123456')
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0407 0000 0000 f5 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0407 0000 0000 f5 00', 'hex')])
})
test('#cmdBleGetPairingMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 040c 0068 0001 87 00 00'))
+ adapter.send.push(Buffer.from('11ef 040c 0068 0001 87 00 00', 'hex'))
// act
const actual = await ultra.cmdBleGetPairingMode()
// assert
expect(actual).toEqual(false)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 040c 0000 0000 f0 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 040c 0000 0000 f0 00', 'hex')])
})
test('#cmdBleSetPairingKey()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0406 0068 0000 8e 00'))
+ adapter.send.push(Buffer.from('11ef 0406 0068 0000 8e 00', 'hex'))
// act
await ultra.cmdBleSetPairingKey('123456')
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0406 0000 0006 f0 313233343536 cb')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0406 0000 0006 f0 313233343536 cb', 'hex')])
})
test('#cmdBleSetPairingMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 040d 0068 0000 87 00'))
+ adapter.send.push(Buffer.from('11ef 040d 0068 0000 87 00', 'hex'))
// act
await ultra.cmdBleSetPairingMode(false)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 040d 0000 0001 ee 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 040d 0000 0001 ee 00 00', 'hex')])
})
test('#cmdChangeDeviceMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex'))
// act
await ultra.cmdChangeDeviceMode(DeviceMode.TAG)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03e9 0000 0001 13 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03e9 0000 0001 13 00 00', 'hex')])
})
test('#cmdEnterBootloader()', async () => {
@@ -113,120 +114,120 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
await ultra.cmdEnterBootloader()
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f2 0000 0000 0b 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f2 0000 0000 0b 00', 'hex')])
})
test('#cmdGetAnimationMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f8 0068 0001 9c 00 00'))
+ adapter.send.push(Buffer.from('11ef 03f8 0068 0001 9c 00 00', 'hex'))
// act
const actual = await ultra.cmdGetAnimationMode()
// assert
expect(actual).toEqual(AnimationMode.FULL)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f8 0000 0000 05 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f8 0000 0000 05 00', 'hex')])
})
test('#cmdGetAppVersion()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e8 0068 0002 ab 0200 fe'))
+ adapter.send.push(Buffer.from('11ef 03e8 0068 0002 ab 0200 fe', 'hex'))
// act
const actual = await ultra.cmdGetAppVersion()
// assert
expect(actual).toEqual('2.0')
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03e8 0000 0000 15 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03e8 0000 0000 15 00', 'hex')])
})
test('#isSupportedAppVersion()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e8 0068 0002 ab 0200 fe'))
+ adapter.send.push(Buffer.from('11ef 03e8 0068 0002 ab 0200 fe', 'hex'))
// act
const actual = await ultra.isSupportedAppVersion()
// assert
expect(actual).toEqual(true)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03e8 0000 0000 15 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03e8 0000 0000 15 00', 'hex')])
})
test('#cmdGetBatteryInfo()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0401 0068 0003 90 105461 3b'))
+ adapter.send.push(Buffer.from('11ef 0401 0068 0003 90 105461 3b', 'hex'))
// act
const actual = await ultra.cmdGetBatteryInfo()
// assert
expect(actual).toEqual({ level: 97, voltage: 4180 })
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0401 0000 0000 fb 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0401 0000 0000 fb 00', 'hex')])
})
test('#cmdGetButtonLongPressAction()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0404 0068 0001 8f 03 fd'))
+ adapter.send.push(Buffer.from('11ef 0404 0068 0001 8f 03 fd', 'hex'))
// act
const actual = await ultra.cmdGetButtonLongPressAction(ButtonType.BUTTON_A)
// assert
expect(actual).toEqual(ButtonAction.CLONE_IC_UID)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0404 0000 0001 f7 41 bf')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0404 0000 0001 f7 41 bf', 'hex')])
})
test('#cmdGetButtonPressAction()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0402 0068 0001 91 01 ff'))
+ adapter.send.push(Buffer.from('11ef 0402 0068 0001 91 01 ff', 'hex'))
// act
const actual = await ultra.cmdGetButtonPressAction(ButtonType.BUTTON_A)
// assert
expect(actual).toEqual(ButtonAction.CYCLE_SLOT_INC)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0402 0000 0001 f9 41 bf')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0402 0000 0001 f9 41 bf', 'hex')])
})
test('#cmdGetDeviceChipId()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f3 0068 0008 9a bbef76355a6a2068 5f'))
+ adapter.send.push(Buffer.from('11ef 03f3 0068 0008 9a bbef76355a6a2068 5f', 'hex'))
// act
const actual = await ultra.cmdGetDeviceChipId()
// assert
expect(actual).toEqual('bbef76355a6a2068')
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f3 0000 0000 0a 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f3 0000 0000 0a 00', 'hex')])
})
test('#cmdGetDeviceMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03ea 0068 0001 aa 00 00'))
+ adapter.send.push(Buffer.from('11ef 03ea 0068 0001 aa 00 00', 'hex'))
// act
const actual = await ultra.cmdGetDeviceMode()
// assert
expect(actual).toEqual(DeviceMode.TAG)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03ea 0000 0000 13 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03ea 0000 0000 13 00', 'hex')])
})
test('#cmdGetDeviceModel()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0409 0068 0001 8a 00 00'))
+ adapter.send.push(Buffer.from('11ef 0409 0068 0001 8a 00 00', 'hex'))
// act
const actual = await ultra.cmdGetDeviceModel()
// assert
expect(actual).toEqual(DeviceModel.ULTRA)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0409 0000 0000 f3 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0409 0000 0000 f3 00', 'hex')])
})
test('#cmdGetDeviceSettings()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 040a 0068 000d 7d 05000102030400313233343536 bc'))
+ adapter.send.push(Buffer.from('11ef 040a 0068 000d 7d 05000102030400313233343536 bc', 'hex'))
// act
const actual = await ultra.cmdGetDeviceSettings()
@@ -240,172 +241,172 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
buttonPressAction: [ButtonAction.CYCLE_SLOT_INC, ButtonAction.CYCLE_SLOT_DEC],
version: 5,
})
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 040a 0000 0000 f2 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 040a 0000 0000 f2 00', 'hex')])
})
test('#cmdGetGitVersion()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f9 0068 0013 89 76322e302e302d3132322d6737666462333538 43'))
+ adapter.send.push(Buffer.from('11ef 03f9 0068 0013 89 76322e302e302d3132322d6737666462333538 43', 'hex'))
// act
const actual = await ultra.cmdGetGitVersion()
// assert
expect(actual).toEqual('v2.0.0-122-g7fdb358')
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f9 0000 0000 04 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f9 0000 0000 04 00', 'hex')])
})
test('#cmdGetSupportedCmds()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 040b 0068 008c fd 03e803e903ea03eb03ec03ed03ee03ef03f003f103f203f303f403f503f603f703f803f903fa03fb03fc03fd03ff0400040104020403040404050407040604080409040a040b040c040d07d007d107d207d307d407d507d607d707d807d907da07db0bb80bb90fa00fa10fa40fa50fa60fa70fa80fa90faa0fab0fac0fad0fae0faf0fb00fb10fb213881389 f9'))
+ adapter.send.push(Buffer.from('11ef 040b 0068 008c fd 03e803e903ea03eb03ec03ed03ee03ef03f003f103f203f303f403f503f603f703f803f903fa03fb03fc03fd03ff0400040104020403040404050407040604080409040a040b040c040d07d007d107d207d307d407d507d607d707d807d907da07db0bb80bb90fa00fa10fa40fa50fa60fa70fa80fa90faa0fab0fac0fad0fae0faf0fb00fb10fb213881389 f9', 'hex'))
// act
const actual = await ultra.cmdGetSupportedCmds()
// assert
expect(actual).toEqual(new Set([1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1031, 1030, 1032, 1033, 1034, 1035, 1036, 1037, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 3000, 3001, 4000, 4001, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015, 4016, 4017, 4018, 5000, 5001]))
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 040b 0000 0000 f1 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 040b 0000 0000 f1 00', 'hex')])
})
test('#cmdResetSettings()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f6 0068 0000 9f 00'))
+ adapter.send.push(Buffer.from('11ef 03f6 0068 0000 9f 00', 'hex'))
// act
await ultra.cmdResetSettings()
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f6 0000 0000 07 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f6 0000 0000 07 00', 'hex')])
})
test('#cmdSaveSettings()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f5 0068 0000 a0 00'))
+ adapter.send.push(Buffer.from('11ef 03f5 0068 0000 a0 00', 'hex'))
// act
await ultra.cmdSaveSettings()
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f5 0000 0000 08 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f5 0000 0000 08 00', 'hex')])
})
test('#cmdSetAnimationMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f7 0068 0000 9e 00'))
+ adapter.send.push(Buffer.from('11ef 03f7 0068 0000 9e 00', 'hex'))
// act
await ultra.cmdSetAnimationMode(AnimationMode.FULL)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f7 0000 0001 05 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f7 0000 0001 05 00 00', 'hex')])
})
test('#cmdSetButtonLongPressAction()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0405 0068 0000 8f 00'))
+ adapter.send.push(Buffer.from('11ef 0405 0068 0000 8f 00', 'hex'))
// act
await ultra.cmdSetButtonLongPressAction(ButtonType.BUTTON_A, ButtonAction.CLONE_IC_UID)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0405 0000 0002 f5 4103 bc')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0405 0000 0002 f5 4103 bc', 'hex')])
})
test('#cmdSetButtonPressAction()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0403 0068 0000 91 00'))
+ adapter.send.push(Buffer.from('11ef 0403 0068 0000 91 00', 'hex'))
// act
await ultra.cmdSetButtonPressAction(ButtonType.BUTTON_A, ButtonAction.CYCLE_SLOT_INC)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0403 0000 0002 f7 4101 be')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0403 0000 0002 f7 4101 be', 'hex')])
})
test('#cmdWipeFds()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03fc 0068 0000 99 00'))
+ adapter.send.push(Buffer.from('11ef 03fc 0068 0000 99 00', 'hex'))
// act
await ultra.cmdWipeFds()
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03fc 0000 0000 01 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03fc 0000 0000 01 00', 'hex')])
})
test('#cmdSlotChangeTagType()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03ec 0068 0000 a9 00'))
+ adapter.send.push(Buffer.from('11ef 03ec 0068 0000 a9 00', 'hex'))
// act
await ultra.cmdSlotChangeTagType(Slot.SLOT_1, TagType.MIFARE_1024)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03ec 0000 0003 0e 0003e9 14')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03ec 0000 0003 0e 0003e9 14', 'hex')])
})
test('#cmdSlotDeleteFreqName()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03fd 0068 0000 98 00'))
+ adapter.send.push(Buffer.from('11ef 03fd 0068 0000 98 00', 'hex'))
// act
const actual = await ultra.cmdSlotDeleteFreqName(Slot.SLOT_1, FreqType.HF)
// assert
expect(actual).toEqual(true)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03fd 0000 0002 fe 0002 fe')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03fd 0000 0002 fe 0002 fe', 'hex')])
})
test('#cmdSlotDeleteFreqName()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03fd 0070 0000 90 00'))
+ adapter.send.push(Buffer.from('11ef 03fd 0070 0000 90 00', 'hex'))
// act
const actual = await ultra.cmdSlotDeleteFreqName(Slot.SLOT_1, FreqType.HF)
// assert
expect(actual).toEqual(false)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03fd 0000 0002 fe 0002 fe')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03fd 0000 0002 fe 0002 fe', 'hex')])
})
test('#cmdSlotDeleteFreqType()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0400 0068 0000 94 00'))
+ adapter.send.push(Buffer.from('11ef 0400 0068 0000 94 00', 'hex'))
// act
await ultra.cmdSlotDeleteFreqType(Slot.SLOT_1, FreqType.HF)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0400 0000 0002 fa 0002 fe')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0400 0000 0002 fa 0002 fe', 'hex')])
})
test('#cmdSlotGetActive()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03fa 0068 0001 9a 00 00'))
+ adapter.send.push(Buffer.from('11ef 03fa 0068 0001 9a 00 00', 'hex'))
// act
const actual = await ultra.cmdSlotGetActive()
// assert
expect(actual).toEqual(Slot.SLOT_1)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03fa 0000 0000 03 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03fa 0000 0000 03 00', 'hex')])
})
test('#cmdSlotGetFreqName()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f0 0068 0005 a0 4d79546167 1e'))
+ adapter.send.push(Buffer.from('11ef 03f0 0068 0005 a0 4d79546167 1e', 'hex'))
// act
const actual = await ultra.cmdSlotGetFreqName(Slot.SLOT_1, FreqType.HF)
// assert
expect(actual).toEqual('MyTag')
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f0 0000 0002 0b 0002 fe')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f0 0000 0002 0b 0002 fe', 'hex')])
})
test('#cmdSlotGetInfo()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03fb 0068 0020 7a 03e9006403e90000000000640000000000000000000000000000000000000000 60'))
+ adapter.send.push(Buffer.from('11ef 03fb 0068 0020 7a 03e9006403e90000000000640000000000000000000000000000000000000000 60', 'hex'))
// act
const actual = await ultra.cmdSlotGetInfo()
@@ -421,12 +422,12 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
{ hfTagType: TagType.UNDEFINED, lfTagType: TagType.UNDEFINED },
{ hfTagType: TagType.UNDEFINED, lfTagType: TagType.UNDEFINED },
])
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03fb 0000 0000 02 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03fb 0000 0000 02 00', 'hex')])
})
test('#cmdSlotGetIsEnable()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03ff 0068 0010 86 01010100000100000000000000000000 fc'))
+ adapter.send.push(Buffer.from('11ef 03ff 0068 0010 86 01010100000100000000000000000000 fc', 'hex'))
// act
const actual = await ultra.cmdSlotGetIsEnable()
@@ -442,77 +443,77 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
{ hf: false, lf: false },
{ hf: false, lf: false },
])
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03ff 0000 0000 fe 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03ff 0000 0000 fe 00', 'hex')])
})
test('#cmdSlotResetTagType()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03ed 0068 0000 a8 00'))
+ adapter.send.push(Buffer.from('11ef 03ed 0068 0000 a8 00', 'hex'))
// act
await ultra.cmdSlotResetTagType(Slot.SLOT_1, TagType.MIFARE_1024)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03ed 0000 0003 0d 0003e9 14')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03ed 0000 0003 0d 0003e9 14', 'hex')])
})
test('#cmdSlotSaveSettings()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03f1 0068 0000 a4 00'))
+ adapter.send.push(Buffer.from('11ef 03f1 0068 0000 a4 00', 'hex'))
// act
await ultra.cmdSlotSaveSettings()
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03f1 0000 0000 0c 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03f1 0000 0000 0c 00', 'hex')])
})
test('#cmdSlotSetActive()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03eb 0068 0000 aa 00'))
+ adapter.send.push(Buffer.from('11ef 03eb 0068 0000 aa 00', 'hex'))
// act
await ultra.cmdSlotSetActive(Slot.SLOT_1)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03eb 0000 0001 11 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03eb 0000 0001 11 00 00', 'hex')])
})
test('#cmdSlotSetEnable()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03ee 0068 0000 a7 00'))
+ adapter.send.push(Buffer.from('11ef 03ee 0068 0000 a7 00', 'hex'))
// act
await ultra.cmdSlotSetEnable(Slot.SLOT_1, FreqType.HF, true)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03ee 0000 0003 0c 000201 fd')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03ee 0000 0003 0c 000201 fd', 'hex')])
})
test('#cmdSlotSetFreqName()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03ef 0068 0000 a6 00'))
+ adapter.send.push(Buffer.from('11ef 03ef 0068 0000 a6 00', 'hex'))
// act
await ultra.cmdSlotSetFreqName(Slot.SLOT_1, FreqType.HF, 'My Tag')
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 03ef 0000 0008 06 00024d7920546167 fc')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 03ef 0000 0008 06 00024d7920546167 fc', 'hex')])
})
test('#cmdEm410xScan()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 0bb8 0040 0005 f8 0000002076 6a'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 0bb8 0040 0005 f8 0000002076 6a', 'hex'))
// act
const actual = await ultra.cmdEm410xScan()
// assert
- expect(actual).toEqual(Buffer.fromHexString('0000002076'))
+ expect(actual).toEqual(Buffer.from('0000002076', 'hex'))
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 0bb8 0000 0000 3d 00'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 0bb8 0000 0000 3d 00', 'hex'),
])
})
@@ -520,17 +521,17 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
expect.hasAssertions()
try {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 0bb8 0041 0000 fc 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 0bb8 0041 0000 fc 00', 'hex'))
// act
const actual = await ultra.cmdEm410xScan()
// assert
- expect(actual).toEqual(Buffer.fromHexString('0000002076'))
+ expect(actual).toEqual(Buffer.from('0000002076'))
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 0bb8 0000 0000 3d 00'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 0bb8 0000 0000 3d 00', 'hex'),
])
} catch (err) {
expect(err.message).toMatch(/tag not found/)
@@ -539,99 +540,99 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
test('#cmdEm410xWriteToT55xx()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 0bb9 0040 0000 fc 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 0bb9 0040 0000 fc 00', 'hex'))
// act
- await ultra.cmdEm410xWriteToT55xx(Buffer.fromHexString('deadbeef88'))
+ await ultra.cmdEm410xWriteToT55xx(Buffer.from('deadbeef88', 'hex'))
// assert
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 0bb9 0000 0011 2b deadbeef88202066665124364819920427 6b'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 0bb9 0000 0011 2b deadbeef88202066665124364819920427 6b', 'hex'),
])
})
test('#cmdHf14aRaw()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0000 1f 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex'))
// act
const actual = await ultra.cmdHf14aRaw({ appendCrc: true, data: Buffer.pack('!H', 0x5000), waitResponse: false })
// assert
- expect(actual).toEqual(Buffer.fromHexString(''))
+ expect(actual).toEqual(new Buffer(0))
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07da 0000 0007 18 2003e800105000 95'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07da 0000 0007 18 2003e800105000 95', 'hex'),
])
})
test('#cmdHf14aScan()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d0 0000 0009 20 0494194a3d04000800 bc'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d0 0000 0009 20 0494194a3d04000800 bc', 'hex'))
// act
const actual = await ultra.cmdHf14aScan()
// assert
expect(actual).toMatchObject([{
- atqa: Buffer.fromHexString('0400'),
- ats: Buffer.fromHexString(''),
- sak: Buffer.fromHexString('08'),
- uid: Buffer.fromHexString('94194a3d'),
+ atqa: Buffer.from('0400', 'hex'),
+ ats: Buffer.from('', 'hex'),
+ sak: Buffer.from('08', 'hex'),
+ uid: Buffer.from('94194a3d', 'hex'),
}])
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d0 0000 0000 29 00'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d0 0000 0000 29 00', 'hex'),
])
})
test('#cmdEm410xGetEmuId()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 1389 0068 0005 f7 deadbeef88 40'))
+ adapter.send.push(Buffer.from('11ef 1389 0068 0005 f7 deadbeef88 40', 'hex'))
// act
const actual = await ultra.cmdEm410xGetEmuId()
// assert
- expect(actual).toMatchObject(Buffer.fromHexString('deadbeef88'))
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 1389 0000 0000 64 00')])
+ expect(actual).toMatchObject(Buffer.from('deadbeef88', 'hex'))
+ expect(adapter.recv).toEqual([Buffer.from('11ef 1389 0000 0000 64 00', 'hex')])
})
test('#cmdEm410xSetEmuId()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 1388 0068 0000 fd 00'))
+ adapter.send.push(Buffer.from('11ef 1388 0068 0000 fd 00', 'hex'))
// act
- await ultra.cmdEm410xSetEmuId(Buffer.fromHexString('deadbeef88'))
+ await ultra.cmdEm410xSetEmuId(Buffer.from('deadbeef88', 'hex'))
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 1388 0000 0005 60 deadbeef88 40')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 1388 0000 0005 60 deadbeef88 40', 'hex')])
})
test('#cmdHf14aGetAntiCollData()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fb2 0068 0009 ce 04deadbeef04000800 b8'))
+ adapter.send.push(Buffer.from('11ef 0fb2 0068 0009 ce 04deadbeef04000800 b8', 'hex'))
// act
const actual = await ultra.cmdHf14aGetAntiCollData()
// assert
expect(actual).toMatchObject({
- atqa: Buffer.fromHexString('0400'),
- ats: Buffer.fromHexString(''),
- sak: Buffer.fromHexString('08'),
- uid: Buffer.fromHexString('deadbeef'),
+ atqa: Buffer.from('0400', 'hex'),
+ ats: Buffer.from('', 'hex'),
+ sak: Buffer.from('08', 'hex'),
+ uid: Buffer.from('deadbeef', 'hex'),
})
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fb2 0000 0000 3f 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fb2 0000 0000 3f 00', 'hex')])
})
test('#cmdHf14aSetAntiCollData()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa1 0068 0000 e8 00'))
+ adapter.send.push(Buffer.from('11ef 0fa1 0068 0000 e8 00', 'hex'))
// act
await ultra.cmdHf14aSetAntiCollData({
@@ -641,162 +642,162 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
})
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa1 0000 0009 47 040102030404000800 e6')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa1 0000 0009 47 040102030404000800 e6', 'hex')])
})
test('#cmdMf1AcquireDarkside()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d4 0000 0021 04 00d3efed0c5499e1c00000000000000000070b0d0a060e0f090000000000000000 62'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d4 0000 0021 04 00d3efed0c5499e1c00000000000000000070b0d0a060e0f090000000000000000 62', 'hex'))
// act
const actual = await ultra.cmdMf1AcquireDarkside(0, Mf1KeyType.KEY_A, true)
// assert
expect(actual).toMatchObject({
- ar: Buffer.fromHexString('00000000'),
- ks: Buffer.fromHexString('070b0d0a060e0f09'),
- nr: Buffer.fromHexString('00000000'),
- nt: Buffer.fromHexString('5499e1c0'),
- par: Buffer.fromHexString('0000000000000000'),
+ ar: Buffer.from('00000000', 'hex'),
+ ks: Buffer.from('070b0d0a060e0f09', 'hex'),
+ nr: Buffer.from('00000000', 'hex'),
+ nt: Buffer.from('5499e1c0', 'hex'),
+ par: Buffer.from('0000000000000000', 'hex'),
status: DarksideStatus.OK,
- uid: Buffer.fromHexString('d3efed0c'),
+ uid: Buffer.from('d3efed0c', 'hex'),
})
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d4 0000 0004 21 6000011e 81'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d4 0000 0004 21 6000011e 81', 'hex'),
])
})
test('#cmdMf1AcquireNested()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d6 0000 0012 11 502e7c41d1b90dab0561d34ecbcd6cef0207 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d6 0000 0012 11 502e7c41d1b90dab0561d34ecbcd6cef0207 00', 'hex'))
// act
const actual = await ultra.cmdMf1AcquireNested(
- { block: 0, keyType: Mf1KeyType.KEY_A, key: Buffer.fromHexString('FFFFFFFFFFFF') },
+ { block: 0, keyType: Mf1KeyType.KEY_A, key: Buffer.from('FFFFFFFFFFFF', 'hex') },
{ block: 4, keyType: Mf1KeyType.KEY_A },
)
// assert
expect(actual).toMatchObject([
- { nt1: Buffer.fromHexString('502e7c41'), nt2: Buffer.fromHexString('d1b90dab'), par: 5 },
- { nt1: Buffer.fromHexString('61d34ecb'), nt2: Buffer.fromHexString('cd6cef02'), par: 7 },
+ { nt1: Buffer.from('502e7c41', 'hex'), nt2: Buffer.from('d1b90dab', 'hex'), par: 5 },
+ { nt1: Buffer.from('61d34ecb', 'hex'), nt2: Buffer.from('cd6cef02', 'hex'), par: 7 },
])
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d6 0000 000a 19 6000ffffffffffff6004 42'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d6 0000 000a 19 6000ffffffffffff6004 42', 'hex'),
])
})
test('#cmdMf1AcquireStaticNested()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d3 0000 0014 12 b908a16d012001458190197501200145cdd400f3 30'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d3 0000 0014 12 b908a16d012001458190197501200145cdd400f3 30', 'hex'))
// act
const actual = await ultra.cmdMf1AcquireStaticNested(
- { block: 0, keyType: Mf1KeyType.KEY_A, key: Buffer.fromHexString('FFFFFFFFFFFF') },
+ { block: 0, keyType: Mf1KeyType.KEY_A, key: Buffer.from('FFFFFFFFFFFF', 'hex') },
{ block: 4, keyType: Mf1KeyType.KEY_A },
)
// assert
expect(actual).toMatchObject({
- uid: Buffer.fromHexString('b908a16d'),
+ uid: Buffer.from('b908a16d', 'hex'),
atks: [
- { nt1: Buffer.fromHexString('01200145'), nt2: Buffer.fromHexString('81901975') },
- { nt1: Buffer.fromHexString('01200145'), nt2: Buffer.fromHexString('cdd400f3') },
+ { nt1: Buffer.from('01200145', 'hex'), nt2: Buffer.from('81901975', 'hex') },
+ { nt1: Buffer.from('01200145', 'hex'), nt2: Buffer.from('cdd400f3', 'hex') },
],
})
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d3 0000 000a 1c 6000ffffffffffff6004 42'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d3 0000 000a 1c 6000ffffffffffff6004 42', 'hex'),
])
})
test('#cmdMf1CheckBlockKey()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d7 0000 0000 22 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d7 0000 0000 22 00', 'hex'))
// act
const actual = await ultra.cmdMf1CheckBlockKey({
block: 0,
- key: Buffer.fromHexString('FFFFFFFFFFFF'),
+ key: Buffer.from('FFFFFFFFFFFF', 'hex'),
keyType: Mf1KeyType.KEY_A,
})
// assert
expect(actual).toEqual(true)
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d7 0000 0008 1a 6000ffffffffffff a6'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d7 0000 0008 1a 6000ffffffffffff a6', 'hex'),
])
})
test('#cmdMf1EmuReadBlock()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa8 0068 0010 d1 deadbeef220804000177a2cc35afa51d 0e'))
+ adapter.send.push(Buffer.from('11ef 0fa8 0068 0010 d1 deadbeef220804000177a2cc35afa51d 0e', 'hex'))
// act
const actual = await ultra.cmdMf1EmuReadBlock(0)
// assert
- expect(actual).toEqual(Buffer.fromHexString('deadbeef220804000177a2cc35afa51d'))
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa8 0000 0002 47 0001 ff')])
+ expect(actual).toEqual(Buffer.from('deadbeef220804000177a2cc35afa51d', 'hex'))
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa8 0000 0002 47 0001 ff', 'hex')])
})
test('#cmdMf1EmuWriteBlock()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa0 0068 0000 e9 00'))
+ adapter.send.push(Buffer.from('11ef 0fa0 0068 0000 e9 00', 'hex'))
// act
- await ultra.cmdMf1EmuWriteBlock(1, Buffer.fromHexString('000102030405060708090a0b0c0d0e0f'))
+ await ultra.cmdMf1EmuWriteBlock(1, Buffer.from('000102030405060708090a0b0c0d0e0f', 'hex'))
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa0 0000 0011 40 01000102030405060708090a0b0c0d0e0f 87')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa0 0000 0011 40 01000102030405060708090a0b0c0d0e0f 87', 'hex')])
})
test('#cmdMf1GetAntiCollMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fae 0068 0001 da 00 00'))
+ adapter.send.push(Buffer.from('11ef 0fae 0068 0001 da 00 00', 'hex'))
// act
const actual = await ultra.cmdMf1GetAntiCollMode()
// assert
expect(actual).toEqual(false)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fae 0000 0000 43 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fae 0000 0000 43 00', 'hex')])
})
test('#cmdMf1GetDetectionCount()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa5 0068 0004 e0 00000003 fd'))
+ adapter.send.push(Buffer.from('11ef 0fa5 0068 0004 e0 00000003 fd', 'hex'))
// act
const actual = await ultra.cmdMf1GetDetectionCount()
// assert
expect(actual).toEqual(3)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa5 0000 0000 4c 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa5 0000 0000 4c 00', 'hex')])
})
test('#cmdMf1GetDetectionEnable()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa7 0068 0001 e1 01 ff'))
+ adapter.send.push(Buffer.from('11ef 0fa7 0068 0001 e1 01 ff', 'hex'))
// act
const actual = await ultra.cmdMf1GetDetectionEnable()
// assert
expect(actual).toEqual(true)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa7 0000 0000 4a 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa7 0000 0000 4a 00', 'hex')])
})
test('#cmdMf1GetDetectionLogs()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa6 0068 0036 ad 03fcb908a16d6bc182304954057b639985b903fcb908a16d47129bc3a78799187711be4e03fcb908a16d5ffb408aac7fd168fd7eb758 25'))
+ adapter.send.push(Buffer.from('11ef 0fa6 0068 0036 ad 03fcb908a16d6bc182304954057b639985b903fcb908a16d47129bc3a78799187711be4e03fcb908a16d5ffb408aac7fd168fd7eb758 25', 'hex'))
// act
const actual = await ultra.cmdMf1GetDetectionLogs(0)
@@ -807,36 +808,36 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
block: 3,
isKeyB: false,
isNested: false,
- ar: Buffer.fromHexString('639985b9'),
- nr: Buffer.fromHexString('4954057b'),
- nt: Buffer.fromHexString('6bc18230'),
- uid: Buffer.fromHexString('b908a16d'),
+ ar: Buffer.from('639985b9', 'hex'),
+ nr: Buffer.from('4954057b', 'hex'),
+ nt: Buffer.from('6bc18230', 'hex'),
+ uid: Buffer.from('b908a16d', 'hex'),
},
{
block: 3,
isKeyB: false,
isNested: false,
- ar: Buffer.fromHexString('7711be4e'),
- nr: Buffer.fromHexString('a7879918'),
- nt: Buffer.fromHexString('47129bc3'),
- uid: Buffer.fromHexString('b908a16d'),
+ ar: Buffer.from('7711be4e', 'hex'),
+ nr: Buffer.from('a7879918', 'hex'),
+ nt: Buffer.from('47129bc3', 'hex'),
+ uid: Buffer.from('b908a16d', 'hex'),
},
{
block: 3,
isKeyB: false,
isNested: false,
- ar: Buffer.fromHexString('fd7eb758'),
- nr: Buffer.fromHexString('ac7fd168'),
- nt: Buffer.fromHexString('5ffb408a'),
- uid: Buffer.fromHexString('b908a16d'),
+ ar: Buffer.from('fd7eb758', 'hex'),
+ nr: Buffer.from('ac7fd168', 'hex'),
+ nt: Buffer.from('5ffb408a', 'hex'),
+ uid: Buffer.from('b908a16d', 'hex'),
},
])
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa6 0000 0004 47 00000000 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa6 0000 0004 47 00000000 00', 'hex')])
})
test('#cmdMf1GetEmuSettings()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa9 0068 0005 db 0100000000 ff'))
+ adapter.send.push(Buffer.from('11ef 0fa9 0068 0005 db 0100000000 ff', 'hex'))
// act
const actual = await ultra.cmdMf1GetEmuSettings()
@@ -849,49 +850,49 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
gen2: false,
write: 0,
})
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa9 0000 0000 48 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa9 0000 0000 48 00', 'hex')])
})
test('#cmdMf1GetGen1aMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0faa 0068 0001 de 00 00'))
+ adapter.send.push(Buffer.from('11ef 0faa 0068 0001 de 00 00', 'hex'))
// act
const actual = await ultra.cmdMf1GetGen1aMode()
// assert
expect(actual).toEqual(false)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0faa 0000 0000 47 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0faa 0000 0000 47 00', 'hex')])
})
test('#cmdMf1GetGen2Mode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fac 0068 0001 dc 00 00'))
+ adapter.send.push(Buffer.from('11ef 0fac 0068 0001 dc 00 00', 'hex'))
// act
const actual = await ultra.cmdMf1GetGen2Mode()
// assert
expect(actual).toEqual(false)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fac 0000 0000 45 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fac 0000 0000 45 00', 'hex')])
})
test('#cmdMf1GetWriteMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fb0 0068 0001 d8 00 00'))
+ adapter.send.push(Buffer.from('11ef 0fb0 0068 0001 d8 00 00', 'hex'))
// act
const actual = await ultra.cmdMf1GetWriteMode()
// assert
expect(actual).toEqual(0)
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fb0 0000 0000 41 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fb0 0000 0000 41 00', 'hex')])
})
test('#cmdMf1IsSupport()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d1 0000 0000 28 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d1 0000 0000 28 00', 'hex'))
// act
const actual = await ultra.cmdMf1IsSupport()
@@ -899,113 +900,113 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(actual).toEqual(true)
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d1 0000 0000 28 00'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d1 0000 0000 28 00', 'hex'),
])
})
test('#cmdMf1ReadBlock()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 877209e11d0804000392abdef258ec90 10'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d8 0000 0010 11 877209e11d0804000392abdef258ec90 10', 'hex'))
// act
const actual = await ultra.cmdMf1ReadBlock({
block: 0,
- key: Buffer.fromHexString('FFFFFFFFFFFF'),
+ key: Buffer.from('FFFFFFFFFFFF', 'hex'),
keyType: Mf1KeyType.KEY_A,
})
// assert
- expect(actual).toEqual(Buffer.fromHexString('877209e11d0804000392abdef258ec90'))
+ expect(actual).toEqual(Buffer.from('877209e11d0804000392abdef258ec90', 'hex'))
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d8 0000 0008 19 6000ffffffffffff a6'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d8 0000 0008 19 6000ffffffffffff a6', 'hex'),
])
})
test('#cmdMf1SetAntiCollMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0faf 0068 0000 da 00'))
+ adapter.send.push(Buffer.from('11ef 0faf 0068 0000 da 00', 'hex'))
// act
await ultra.cmdMf1SetAntiCollMode(false)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0faf 0000 0001 41 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0faf 0000 0001 41 00 00', 'hex')])
})
test('#cmdMf1SetDetectionEnable()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fa4 0068 0000 e5 00'))
+ adapter.send.push(Buffer.from('11ef 0fa4 0068 0000 e5 00', 'hex'))
// act
await ultra.cmdMf1SetDetectionEnable(true)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fa4 0000 0001 4c 01 ff')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fa4 0000 0001 4c 01 ff', 'hex')])
})
test('#cmdMf1SetGen1aMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fab 0068 0000 de 00'))
+ adapter.send.push(Buffer.from('11ef 0fab 0068 0000 de 00', 'hex'))
// act
await ultra.cmdMf1SetGen1aMode(false)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fab 0000 0001 45 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fab 0000 0001 45 00 00', 'hex')])
})
test('#cmdMf1SetGen2Mode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fad 0068 0000 dc 00'))
+ adapter.send.push(Buffer.from('11ef 0fad 0068 0000 dc 00', 'hex'))
// act
await ultra.cmdMf1SetGen2Mode(false)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fad 0000 0001 43 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fad 0000 0001 43 00 00', 'hex')])
})
test('#cmdMf1SetWriteMode()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 0fb1 0068 0000 d8 00'))
+ adapter.send.push(Buffer.from('11ef 0fb1 0068 0000 d8 00', 'hex'))
// act
await ultra.cmdMf1SetWriteMode(Mf1EmuWriteMode.NORMAL)
// assert
- expect(adapter.recv).toEqual([Buffer.fromHexString('11ef 0fb1 0000 0001 3f 00 00')])
+ expect(adapter.recv).toEqual([Buffer.from('11ef 0fb1 0000 0001 3f 00 00', 'hex')])
})
test('#cmdMf1TestNtDistance()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d5 0000 0008 1c 877209e100000080 9d'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d5 0000 0008 1c 877209e100000080 9d', 'hex'))
// act
const actual = await ultra.cmdMf1TestNtDistance({
block: 0,
- key: Buffer.fromHexString('FFFFFFFFFFFF'),
+ key: Buffer.from('FFFFFFFFFFFF', 'hex'),
keyType: Mf1KeyType.KEY_A,
})
// assert
expect(actual).toMatchObject({
- dist: Buffer.fromHexString('00000080'),
- uid: Buffer.fromHexString('877209e1'),
+ dist: Buffer.from('00000080', 'hex'),
+ uid: Buffer.from('877209e1', 'hex'),
})
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d5 0000 0008 1c 6000ffffffffffff a6'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d5 0000 0008 1c 6000ffffffffffff a6', 'hex'),
])
})
test('#cmdMf1TestPrngType()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d2 0000 0001 26 01 ff'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d2 0000 0001 26 01 ff', 'hex'))
// act
const actual = await ultra.cmdMf1TestPrngType()
@@ -1013,16 +1014,16 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(actual).toEqual(Mf1PrngType.WEAK)
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d2 0000 0000 27 00'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d2 0000 0000 27 00', 'hex'),
])
})
test('#cmdMf1VblockManipulate()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07db 0000 0000 1e 00'))
- const key = Buffer.fromHexString('FFFFFFFFFFFF')
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07db 0000 0000 1e 00', 'hex'))
+ const key = Buffer.from('FFFFFFFFFFFF', 'hex')
// act
await ultra.cmdMf1VblockManipulate(
@@ -1033,37 +1034,37 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07db 0000 0015 09 6004ffffffffffffc0000000016004ffffffffffff 83'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07db 0000 0015 09 6004ffffffffffffc0000000016004ffffffffffff 83', 'hex'),
])
})
test('#cmdMf1WriteBlock()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d9 0000 0000 20 00', 'hex'))
// act
await ultra.cmdMf1WriteBlock({
block: 4,
keyType: Mf1KeyType.KEY_A,
- key: Buffer.fromHexString('FFFFFFFFFFFF'),
- data: Buffer.fromHexString('00000000000000000000000000000000'),
+ key: Buffer.from('FFFFFFFFFFFF', 'hex'),
+ data: Buffer.from('00000000000000000000000000000000', 'hex'),
})
// assert
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d9 0000 0018 08 6004ffffffffffff00000000000000000000000000000000 a2'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d9 0000 0018 08 6004ffffffffffff00000000000000000000000000000000 a2', 'hex'),
])
})
test('#hf14aInfo()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d0 0000 0009 20 0494194a3d04000800 bc')) // cmdHf14aScan
- adapter.send.push(Buffer.fromHexString('11ef 07d1 0000 0000 28 00')) // cmdMf1IsSupport
- adapter.send.push(Buffer.fromHexString('11ef 07d2 0000 0001 26 01 ff')) // cmdMf1TestPrngType
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d0 0000 0009 20 0494194a3d04000800 bc', 'hex')) // cmdHf14aScan
+ adapter.send.push(Buffer.from('11ef 07d1 0000 0000 28 00', 'hex')) // cmdMf1IsSupport
+ adapter.send.push(Buffer.from('11ef 07d2 0000 0001 26 01 ff', 'hex')) // cmdMf1TestPrngType
// act
const actual = await ultra.hf14aInfo()
@@ -1071,10 +1072,10 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(actual).toMatchObject([{
antiColl: {
- atqa: Buffer.fromHexString('0400'),
- ats: Buffer.fromHexString(''),
- sak: Buffer.fromHexString('08'),
- uid: Buffer.fromHexString('94194a3d'),
+ atqa: Buffer.from('0400', 'hex'),
+ ats: Buffer.from('', 'hex'),
+ sak: Buffer.from('08', 'hex'),
+ uid: Buffer.from('94194a3d', 'hex'),
},
nxpTypeBySak: 'MIFARE Classic 1K | Plus SE 1K | Plug S 2K | Plus X 2K',
prngType: Mf1PrngType.WEAK,
@@ -1083,142 +1084,142 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
test('#mf1VblockGetValue()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 01000000feffffff0100000001fe01fe 05'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d8 0000 0010 11 01000000feffffff0100000001fe01fe 05', 'hex'))
// act
const actual = await ultra.mf1VblockGetValue({
block: 1,
- key: Buffer.fromHexString('FFFFFFFFFFFF'),
+ key: Buffer.from('FFFFFFFFFFFF', 'hex'),
keyType: Mf1KeyType.KEY_A,
})
// assert
expect(actual).toMatchObject({ adr: 1, value: 1 })
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d8 0000 0008 19 6001ffffffffffff a5'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d8 0000 0008 19 6001ffffffffffff a5', 'hex'),
])
})
test('#mf1VblockSetValue()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07d9 0000 0000 20 00', 'hex'))
// act
await ultra.mf1VblockSetValue({
block: 4,
keyType: Mf1KeyType.KEY_A,
- key: Buffer.fromHexString('FFFFFFFFFFFF'),
+ key: Buffer.from('FFFFFFFFFFFF', 'hex'),
}, { adr: 1, value: 1 })
// assert
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07d9 0000 0018 08 6004ffffffffffff01000000feffffff0100000001fe01fe a7'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07d9 0000 0018 08 6004ffffffffffff01000000feffffff0100000001fe01fe a7', 'hex'),
])
})
test('#mfuReadPages()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0010 0f 040dc445420d2981e7480000e1100600 c7'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07da 0000 0010 0f 040dc445420d2981e7480000e1100600 c7', 'hex'))
// act
const actual = await ultra.mfuReadPages({ pageOffset: 0 })
// assert
- expect(actual).toEqual(Buffer.fromHexString('040dc445420d2981e7480000e1100600'))
+ expect(actual).toEqual(Buffer.from('040dc445420d2981e7480000e1100600', 'hex'))
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07da 0000 0007 18 7403e800103000 61'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07da 0000 0007 18 7403e800103000 61', 'hex'),
])
})
test('#mfuWritePage()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0000 1f 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex'))
// act
- await ultra.mfuWritePage({ pageOffset: 9, data: Buffer.fromHexString('00000000') })
+ await ultra.mfuWritePage({ pageOffset: 9, data: Buffer.from('00000000', 'hex') })
// assert
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07da 0000 000b 14 7403e80030a20900000000 c6'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07da 0000 000b 14 7403e80030a20900000000 c6', 'hex'),
])
})
test('#mf1Halt()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0000 1f 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex'))
// act
await ultra.mf1Halt()
// assert
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07da 0000 0007 18 2003e800105000 95'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07da 0000 0007 18 2003e800105000 95', 'hex'),
])
})
test('#mf1Gen1aReadBlocks()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0000 1f 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0001 1e 0a f6'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0001 1e 0a f6'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0010 0f 877209e11d0804000392abdef258ec90 10'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0000 1f 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0001 1e 0a f6', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0001 1e 0a f6', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0010 0f 877209e11d0804000392abdef258ec90 10', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex'))
// act
const actual = await ultra.mf1Gen1aReadBlocks(0, 1)
// assert
- expect(actual).toEqual(Buffer.fromHexString('877209e11d0804000392abdef258ec90'))
+ expect(actual).toEqual(Buffer.from('877209e11d0804000392abdef258ec90', 'hex'))
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07da 0000 0007 18 2003e800105000 95'),
- Buffer.fromHexString('11ef 07da 0000 0006 19 4803e8000740 86'),
- Buffer.fromHexString('11ef 07da 0000 0006 19 4803e8000843 82'),
- Buffer.fromHexString('11ef 07da 0000 0007 18 6c03e800103000 69'),
- Buffer.fromHexString('11ef 07da 0000 0007 18 2003e800105000 95'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07da 0000 0007 18 2003e800105000 95', 'hex'),
+ Buffer.from('11ef 07da 0000 0006 19 4803e8000740 86', 'hex'),
+ Buffer.from('11ef 07da 0000 0006 19 4803e8000843 82', 'hex'),
+ Buffer.from('11ef 07da 0000 0007 18 6c03e800103000 69', 'hex'),
+ Buffer.from('11ef 07da 0000 0007 18 2003e800105000 95', 'hex'),
])
})
test('#mf1Gen1aWriteBlocks()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0000 1f 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0001 1e 0a f6'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0001 1e 0a f6'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0001 1e 0a f6'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0001 1e 0a f6'))
- adapter.send.push(Buffer.fromHexString('11ef 07da 0000 0000 1f 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0001 1e 0a f6', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0001 1e 0a f6', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0001 1e 0a f6', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0001 1e 0a f6', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07da 0000 0000 1f 00', 'hex'))
// act
await ultra.mf1Gen1aWriteBlocks(1, new Buffer(16))
// assert
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07da 0000 0007 18 2003e800105000 95'),
- Buffer.fromHexString('11ef 07da 0000 0006 19 4803e8000740 86'),
- Buffer.fromHexString('11ef 07da 0000 0006 19 4803e8000843 82'),
- Buffer.fromHexString('11ef 07da 0000 0007 18 6803e80010a001 fc'),
- Buffer.fromHexString('11ef 07da 0000 0015 0a 6803e8008000000000000000000000000000000000 2d'),
- Buffer.fromHexString('11ef 07da 0000 0007 18 2003e800105000 95'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07da 0000 0007 18 2003e800105000 95', 'hex'),
+ Buffer.from('11ef 07da 0000 0006 19 4803e8000740 86', 'hex'),
+ Buffer.from('11ef 07da 0000 0006 19 4803e8000843 82', 'hex'),
+ Buffer.from('11ef 07da 0000 0007 18 6803e80010a001 fc', 'hex'),
+ Buffer.from('11ef 07da 0000 0015 0a 6803e8008000000000000000000000000000000000 2d', 'hex'),
+ Buffer.from('11ef 07da 0000 0007 18 2003e800105000 95', 'hex'),
])
})
test('#mf1CheckSectorKeys()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07dc 0000 01ea 32 c0000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 4c'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07dc 0000 01ea 32 c0000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 4c', 'hex'))
// act
const keys = Buffer.from('FFFFFFFFFFFF\n000000000000\nA0A1A2A3A4A5\nD3F7D3F7D3F7', 'hex').chunk(6)
@@ -1226,23 +1227,23 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(actual).toMatchObject({
- [Mf1KeyType.KEY_A]: Buffer.fromHexString('FFFFFFFFFFFF'),
- [Mf1KeyType.KEY_B]: Buffer.fromHexString('FFFFFFFFFFFF'),
+ [Mf1KeyType.KEY_A]: Buffer.from('FFFFFFFFFFFF', 'hex'),
+ [Mf1KeyType.KEY_B]: Buffer.from('FFFFFFFFFFFF', 'hex'),
})
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07dc 0000 0022 fb 3fffffffffffffffffffffffffffffff000000000000a0a1a2a3a4a5d3f7d3f7d3f7 a3'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07dc 0000 0022 fb 3fffffffffffffffffffffffffffffff000000000000a0a1a2a3a4a5d3f7d3f7d3f7 a3', 'hex'),
])
})
test('#mf1ReadSectorByKeys()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07dc 0000 01ea 32 c0000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 4c'))
- adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 877209e11d0804000392abdef258ec90 10'))
- adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 00000000000000000000000000000000 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 00000000000000000000000000000000 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d8 0000 0010 11 000000000000ff078069ffffffffffff 17'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07dc 0000 01ea 32 c0000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 4c', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d8 0000 0010 11 877209e11d0804000392abdef258ec90 10', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d8 0000 0010 11 00000000000000000000000000000000 00', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d8 0000 0010 11 00000000000000000000000000000000 00', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d8 0000 0010 11 000000000000ff078069ffffffffffff 17', 'hex'))
// act
const keys = Buffer.from('FFFFFFFFFFFF\n000000000000\nA0A1A2A3A4A5\nD3F7D3F7D3F7', 'hex').chunk(6)
@@ -1251,31 +1252,31 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(actual).toEqual({
data: Buffer.concat([
- Buffer.fromHexString('877209e11d0804000392abdef258ec90'),
- Buffer.fromHexString('00000000000000000000000000000000'),
- Buffer.fromHexString('00000000000000000000000000000000'),
- Buffer.fromHexString('ffffffffffffff078069ffffffffffff'),
+ Buffer.from('877209e11d0804000392abdef258ec90', 'hex'),
+ Buffer.from('00000000000000000000000000000000', 'hex'),
+ Buffer.from('00000000000000000000000000000000', 'hex'),
+ Buffer.from('ffffffffffffff078069ffffffffffff', 'hex'),
]),
success: [true, true, true, true],
})
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07dc 0000 0022 fb 3fffffffffffffffffffffffffffffff000000000000a0a1a2a3a4a5d3f7d3f7d3f7 a3'),
- Buffer.fromHexString('11ef 07d8 0000 0008 19 6100ffffffffffff a5'),
- Buffer.fromHexString('11ef 07d8 0000 0008 19 6101ffffffffffff a4'),
- Buffer.fromHexString('11ef 07d8 0000 0008 19 6102ffffffffffff a3'),
- Buffer.fromHexString('11ef 07d8 0000 0008 19 6103ffffffffffff a2'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07dc 0000 0022 fb 3fffffffffffffffffffffffffffffff000000000000a0a1a2a3a4a5d3f7d3f7d3f7 a3', 'hex'),
+ Buffer.from('11ef 07d8 0000 0008 19 6100ffffffffffff a5', 'hex'),
+ Buffer.from('11ef 07d8 0000 0008 19 6101ffffffffffff a4', 'hex'),
+ Buffer.from('11ef 07d8 0000 0008 19 6102ffffffffffff a3', 'hex'),
+ Buffer.from('11ef 07d8 0000 0008 19 6103ffffffffffff a2', 'hex'),
])
})
test('#mf1WriteSectorByKeys()', async () => {
// arrange
- adapter.send.push(Buffer.fromHexString('11ef 03e9 0068 0000 ac 00')) // DeviceMode.READER
- adapter.send.push(Buffer.fromHexString('11ef 07dc 0000 01ea 32 30000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 dc'))
- adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
- adapter.send.push(Buffer.fromHexString('11ef 07d9 0000 0000 20 00'))
+ adapter.send.push(Buffer.from('11ef 03e9 0068 0000 ac 00', 'hex')) // DeviceMode.READER
+ adapter.send.push(Buffer.from('11ef 07dc 0000 01ea 32 30000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 dc', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d9 0000 0000 20 00', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d9 0000 0000 20 00', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d9 0000 0000 20 00', 'hex'))
+ adapter.send.push(Buffer.from('11ef 07d9 0000 0000 20 00', 'hex'))
// act
const keys = Buffer.from('FFFFFFFFFFFF\n000000000000\nA0A1A2A3A4A5\nD3F7D3F7D3F7', 'hex').chunk(6)
@@ -1290,12 +1291,12 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
// assert
expect(actual).toEqual({ success: [true, true, true, true] })
expect(adapter.recv).toEqual([
- Buffer.fromHexString('11ef 03e9 0000 0001 13 01 ff'), // DeviceMode.READER
- Buffer.fromHexString('11ef 07dc 0000 0022 fb cfffffffffffffffffffffffffffffff000000000000a0a1a2a3a4a5d3f7d3f7d3f7 13'),
- Buffer.fromHexString('11ef 07d9 0000 0018 08 6104ffffffffffff00000000000000000000000000000000 a1'),
- Buffer.fromHexString('11ef 07d9 0000 0018 08 6105ffffffffffff00000000000000000000000000000000 a0'),
- Buffer.fromHexString('11ef 07d9 0000 0018 08 6106ffffffffffff00000000000000000000000000000000 9f'),
- Buffer.fromHexString('11ef 07d9 0000 0018 08 6107ffffffffffffffffffffffffff078069ffffffffffff bb'),
+ Buffer.from('11ef 03e9 0000 0001 13 01 ff', 'hex'), // DeviceMode.READER
+ Buffer.from('11ef 07dc 0000 0022 fb cfffffffffffffffffffffffffffffff000000000000a0a1a2a3a4a5d3f7d3f7d3f7 13', 'hex'),
+ Buffer.from('11ef 07d9 0000 0018 08 6104ffffffffffff00000000000000000000000000000000 a1', 'hex'),
+ Buffer.from('11ef 07d9 0000 0018 08 6105ffffffffffff00000000000000000000000000000000 a0', 'hex'),
+ Buffer.from('11ef 07d9 0000 0018 08 6106ffffffffffff00000000000000000000000000000000 9f', 'hex'),
+ Buffer.from('11ef 07d9 0000 0018 08 6107ffffffffffffffffffffffffff078069ffffffffffff bb', 'hex'),
])
})
@@ -1307,7 +1308,7 @@ describe('ChameleonUltra with BufferMockAdapter', () => {
{ acl: '45687B3167880400C810002000000016 0E140001070208030904081000000000 00000000000000000000000000000000 BBF8D48E031978778869C19085AF2635', expected: true },
])('#mf1IsValidAcl($acl) = $expected', async ({ acl, expected }) => {
// act
- const actual = ultra.mf1IsValidAcl(Buffer.fromHexString(acl))
+ const actual = ultra.mf1IsValidAcl(Buffer.from(acl, 'hex'))
// assert
expect(actual).toBe(expected)
diff --git a/src/ChameleonUltra.ts b/src/ChameleonUltra.ts
index 5e417e1..97eb04a 100644
--- a/src/ChameleonUltra.ts
+++ b/src/ChameleonUltra.ts
@@ -1,9 +1,10 @@
import _ from 'lodash'
import { Buffer } from '@taichunmin/buffer'
import { errToJson, middlewareCompose, sleep, type MiddlewareComposeFn, versionCompare } from './helper'
-import { type ReadableStream, type UnderlyingSink, WritableStream } from 'node:stream/web'
+import { EventAsyncGenerator } from './EventAsyncGenerator'
+import { EventEmitter } from './EventEmitter'
+import { type ReadableStream, type UnderlyingSink, type WritableStreamDefaultController, WritableStream } from 'node:stream/web'
import * as Decoder from './ResponseDecoder'
-import createDebugger, { type Debugger } from 'debug'
import {
Cmd,
@@ -36,7 +37,6 @@ import {
const READ_DEFAULT_TIMEOUT = 5e3
const START_OF_FRAME = new Buffer(2).writeUInt16BE(0x11EF)
const VERSION_SUPPORTED = { gte: '2.0', lt: '3.0' } as const
-
const WritableStream1: typeof WritableStream = (globalThis as any)?.WritableStream ?? WritableStream
function isMf1BlockNo (block: any): boolean {
@@ -50,31 +50,96 @@ function validateMf1BlockKey (block: any, keyType: any, key: any, prefix: string
}
/**
- * The core library of "chameleon-ultra.js". The instance of this class must use exactly one adapter plugin to communication to ChameleonUltra.
+ * The core library of `chameleon-ultra.js`. You need to register exactly one adapter to the `ChameleonUltra` instance.
+ *
+ * @see You can learn how to use `@taichunmin/buffer` from {@link https://taichunmin.idv.tw/js-buffer/ | here}.
+ * @example
+ *
+ * Click here to expend import example.
+ *
+ * Example of import the library using `import` or `require`:
+ *
+ * ```js
+ * // import
+ * import { Buffer, ChameleonUltra } from 'chameleon-ultra.js'
+ * import SerialPortAdapter from 'chameleon-ultra.js/plugin/SerialPortAdapter'
+ * import WebbleAdapter from 'chameleon-ultra.js/plugin/WebbleAdapter'
+ * import WebserialAdapter from 'chameleon-ultra.js/plugin/WebserialAdapter'
+ *
+ * // require
+ * const { Buffer, ChameleonUltra } = require('chameleon-ultra.js')
+ * const SerialPortAdapter = require('chameleon-ultra.js/plugin/SerialPortAdapter')
+ * const WebbleAdapter = require('chameleon-ultra.js/plugin/WebbleAdapter')
+ * const WebserialAdapter = require('chameleon-ultra.js/plugin/WebserialAdapter')
+ * ```
+ *
+ * Example of import the library in Browser (place at the end of body):
+ *
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * ```
+ *
+ * After importing the SDK, you need to register exactly one adapter to the `ChameleonUltra` instance:
+ *
+ * ```js
+ * const ultraUsb = new ChameleonUltra()
+ * ultraUsb.use(new WebserialAdapter())
+ * const ultraBle = new ChameleonUltra()
+ * ultraBle.use(new WebbleAdapter())
+ * ```
+ *
+ *
*/
export class ChameleonUltra {
+ #deviceMode: DeviceMode | null = null
#isDisconnecting: boolean = false
#rxSink?: ChameleonRxSink
#supportedCmds: Set = new Set()
- readonly #debug: boolean
+ readonly #hooks = new Map>()
+ readonly #middlewares = new Map()
/**
- * @internal
- * @group Internal
+ * The supported version of SDK.
+ * @group Device Related
*/
- hooks: Record
+ static VERSION_SUPPORTED = VERSION_SUPPORTED
/**
* @internal
* @group Internal
*/
- logger: Record = {}
+ readDefaultTimeout: number = READ_DEFAULT_TIMEOUT
/**
+ * The event emitter of `ChameleonUltra`.
+ * - `disconnected`: Emitted when device is disconnected.
+ * - `connected`: Emitted when device is connected.
+ * - `debug`: Emitted when debug message is generated. `(logName: string, formatter: any, ...args: [] | any[]) => void`
* @internal
* @group Internal
*/
- plugins: Map
+ readonly emitter = new EventEmitter()
/**
* @internal
@@ -82,93 +147,8 @@ export class ChameleonUltra {
*/
port?: ChameleonSerialPort
- /**
- * @internal
- * @group Internal
- */
- #deviceMode: DeviceMode | null = null
-
- /**
- * The supported version of SDK.
- * @group Device Related
- */
- static VERSION_SUPPORTED = VERSION_SUPPORTED
-
- /**
- * Create a new instance of ChameleonUltra.
- * @param debug - Enable debug mode.
- * @example
- * Example usage in Browser (place at the end of body):
- *
- * ```html
- *
- * ```
- *
- * Example usage in CommonJS:
- *
- * ```js
- * const { Buffer, ChameleonUltra } = require('chameleon-ultra.js')
- * const SerialPortAdapter = require('chameleon-ultra.js/plugin/SerialPortAdapter')
- * const WebbleAdapter = require('chameleon-ultra.js/plugin/WebbleAdapter')
- * const WebserialAdapter = require('chameleon-ultra.js/plugin/WebserialAdapter')
- *
- * const ultraNode = new ChameleonUltra()
- * ultraNode.use(new SerialPortAdapter())
- *
- * const ultraUsb = new ChameleonUltra()
- * ultraUsb.use(new WebserialAdapter())
- *
- * const ultraBle = new ChameleonUltra()
- * ultraBle.use(new WebbleAdapter())
- * ```
- *
- * Example usage in ESM:
- *
- * ```js
- * import { Buffer, ChameleonUltra } from 'chameleon-ultra.js'
- * import SerialPortAdapter from 'chameleon-ultra.js/plugin/SerialPortAdapter'
- * import WebbleAdapter from 'chameleon-ultra.js/plugin/WebbleAdapter'
- * import WebserialAdapter from 'chameleon-ultra.js/plugin/WebserialAdapter'
- *
- * const ultraNode = new ChameleonUltra()
- * ultraNode.use(new SerialPortAdapter())
- *
- * const ultraUsb = new ChameleonUltra()
- * ultraUsb.use(new WebserialAdapter())
- *
- * const ultraBle = new ChameleonUltra()
- * ultraBle.use(new WebbleAdapter())
- * ```
- */
- constructor (debug = false) {
- this.hooks = {}
- this.plugins = new Map()
- this.#debug = debug
- _.extend(this.logger, {
- core: this.createDebugger('core'),
- resp: this.createDebugger('resp'),
- respError: this.createDebugger('respError'),
- send: this.createDebugger('send'),
- })
- }
-
- /**
- * @internal
- * @group Plugin Related
- */
- createDebugger (name: string): Logger {
- if (!this.#debug) return (...args: any[]) => {}
- return createDebugger(`ultra:${name}`)
+ #debug (namespace: string, formatter: any, ...args: [] | any[]): void {
+ this.emitter.emit('debug', namespace, formatter, ...args)
}
/**
@@ -186,27 +166,30 @@ export class ChameleonUltra {
/**
* Register a hook.
- * @param hook - The hook name.
+ * @param hookName - The hook name.
* @param fn - The function to register.
* @group Plugin Related
*/
- addHook (hook: string, fn: MiddlewareComposeFn): this {
- if (!_.isArray(this.hooks[hook])) this.hooks[hook] = []
- this.hooks[hook].push(fn)
+ addHook (hookName: string, fn: MiddlewareComposeFn): this {
+ const middlewares = this.#middlewares.get(hookName) ?? []
+ if (!this.#middlewares.has(hookName)) this.#middlewares.set(hookName, middlewares)
+ middlewares.push(fn)
+ this.#hooks.set(hookName, middlewareCompose(middlewares))
return this
}
/**
* Invoke a hook with context.
- * @param hook - The hook name.
+ * @param hookName - The hook name.
* @param ctx - The context will be passed to every middleware.
* @param next - The next middleware function.
* @returns The return value depent on the middlewares
* @group Plugin Related
*/
- async invokeHook (hook: string, ctx: any = {}, next?: MiddlewareComposeFn): Promise {
- ctx.me = this
- return await middlewareCompose(this.hooks[hook] ?? [])(ctx, next)
+ async invokeHook (hookName: string, ctx: any = {}, next?: MiddlewareComposeFn): Promise {
+ const hook = this.#hooks.get(hookName) ?? middlewareCompose([])
+ if (!this.#hooks.has(hookName)) this.#hooks.set(hookName, hook)
+ return await hook({ ...ctx, ultra: this }, next)
}
/**
@@ -219,19 +202,19 @@ export class ChameleonUltra {
if (_.isNil(this.port)) throw new Error('this.port is undefined. Did you remember to use adapter plugin?')
// serial.readable pipeTo this.rxSink
- this.#rxSink = new ChameleonRxSink()
- void this.port.readable.pipeTo(new WritableStream1(this.#rxSink), {
- signal: this.#rxSink.signal,
- }).catch(async err => {
- if (err.message === 'disconnect()') return // disconnected by invoke disconnect()
- await this.disconnect(_.merge(new Error(`Failed to read resp: ${err.message}`), { originalError: err }))
- })
-
- this.logger.core('chameleon connected')
+ const promiseConnected = new Promise(resolve => this.emitter.once('connected', resolve))
+ this.#rxSink = new ChameleonRxSink(this)
+ void this.port.readable.pipeTo(new WritableStream1(this.#rxSink), this.#rxSink.abortController)
+ .catch(err => { this.#debug('rxSink', err) })
+
+ const connectedAt = await promiseConnected
+ this.#debug('core', `connected at ${connectedAt.toISOString()}`)
} catch (err) {
- this.logger.core(`Failed to connect: ${err.message as string}`)
+ err.message = `Failed to connect: ${err.message}`
+ this.emitter.emit('error', err)
+ this.#debug('error', `${err.message}\n${err.stack}`)
if (this.isConnected()) await this.disconnect(err)
- throw _.merge(new Error(err.message ?? 'Failed to connect'), { originalError: err })
+ throw err
}
})
}
@@ -243,20 +226,26 @@ export class ChameleonUltra {
async disconnect (err: Error = new Error('disconnect()')): Promise {
try {
if (this.#isDisconnecting) return
- this.logger.core('%s %O', err.message, errToJson(err))
this.#isDisconnecting = true // 避免重複執行
+ this.#debug('core', '%s %O', err.message, errToJson(err))
+ this.#debug('core', 'disconnecting...')
await this.invokeHook('disconnect', { err }, async (ctx, next) => {
try {
// clean up
this.#deviceMode = null
this.#supportedCmds.clear()
- // close port
- this.#rxSink?.controller.abort(err)
+ const promiseDisconnected = new Promise<[Date, string | undefined]>(resolve => {
+ this.emitter.once('disconnected', (disconnected: Date, reason?: string) => { resolve([disconnected, reason]) })
+ })
+ this.#rxSink?.abortController.abort(err)
while (this.port?.readable?.locked === true) await sleep(10)
await this.port?.readable?.cancel(err)
await this.port?.writable?.close()
delete this.port
+
+ const [disconnectedAt, reason] = await promiseDisconnected
+ this.#debug('core', `disconnected at ${disconnectedAt.toISOString()}, reason = ${reason ?? '?'}`)
} catch (err) {
throw _.merge(new Error(err.message ?? 'Failed to disconnect'), { originalError: err })
}
@@ -274,31 +263,20 @@ export class ChameleonUltra {
return this?.port?.isOpen?.() ?? false
}
- /**
- * Calculate the LRC byte of a buffer.
- * @internal
- * @group Internal
- */
- _calcLrc (buf: Buffer): number {
- return 0x100 - _.sum(buf) & 0xFF
- }
-
/**
* Send a buffer to device.
* @param buf - The buffer to be sent to device.
* @internal
* @group Internal
*/
- async _writeBuffer (buf: Buffer): Promise {
- await this.invokeHook('_writeBuffer', { buf }, async (ctx, next) => {
- if (!Buffer.isBuffer(ctx.buf)) throw new TypeError('buf should be a Buffer')
- if (!this.isConnected()) await this.connect()
- this.logger.send(ChameleonUltraFrame.inspect(ctx.buf))
- const writer = (this.port?.writable as any)?.getWriter()
- if (_.isNil(writer)) throw new Error('Failed to getWriter(). Did you remember to use adapter plugin?')
- await writer.write(ctx.buf)
- writer.releaseLock()
- })
+ async #sendBuffer (buf: Buffer): Promise {
+ if (!Buffer.isBuffer(buf)) throw new TypeError('buf should be a Buffer')
+ if (!this.isConnected()) await this.connect()
+ this.#debug('send', ChameleonUltraFrame.inspect(buf))
+ const writer = (this.port?.writable as any)?.getWriter()
+ if (_.isNil(writer)) throw new Error('Failed to getWriter(). Did you remember to use adapter plugin?')
+ await writer.write(buf)
+ writer.releaseLock()
}
/**
@@ -309,34 +287,16 @@ export class ChameleonUltra {
* @internal
* @group Internal
*/
- async _writeCmd (opts: {
+ async #sendCmd (opts: {
cmd: Cmd
status?: RespStatus
data?: Buffer
}): Promise {
const { cmd, status = 0, data = Buffer.allocUnsafe(0) } = opts
- const buf = Buffer.allocUnsafe(data.length + 10)
- START_OF_FRAME.copy(buf, 0) // SOF + SOF LRC Byte
- // head info
- buf.writeUInt16BE(cmd, 2)
- buf.writeUInt16BE(status, 4)
- buf.writeUInt16BE(data.length, 6)
- buf[8] = this._calcLrc(buf.subarray(2, 8)) // head lrc byte
- // data
- if (data.length > 0) data.copy(buf, 9)
- // lrc byte of buf
- buf[buf.length - 1] = this._calcLrc(buf.subarray(9, -1))
- await this._writeBuffer(buf)
- }
-
- /**
- * Return the buffers in rxSink and clear rxSink.
- * @returns The buffers in rxSink.
- * @internal
- * @group Internal
- */
- _clearRxBufs (): Buffer[] {
- return this.#rxSink?.bufs.splice(0, this.#rxSink.bufs.length) ?? []
+ const buf = Buffer.pack(`!2sHHHx${data.length}sx`, START_OF_FRAME, cmd, status, data.length, data)
+ buf[8] = bufLrc(buf.subarray(2, 8)) // head lrc byte
+ buf[buf.length - 1] = bufLrc(buf.subarray(9, -1)) // lrc of buf
+ await this.#sendBuffer(buf)
}
/**
@@ -345,56 +305,49 @@ export class ChameleonUltra {
* @internal
* @group Internal
*/
- async _readRespTimeout ({ cmd, timeout }: { cmd?: Cmd, timeout?: number } = {}): Promise {
- interface Context {
- startedAt?: number
- nowts?: number
- timeout?: number
- resp?: ChameleonUltraFrame
- }
- return await this.invokeHook('_readRespTimeout', { timeout }, async (ctx: Context, next) => {
+ async #createReadRespFn (args: {
+ cmd?: Cmd
+ filter?: (resp: ChameleonUltraFrame) => boolean
+ timeout?: number
+ }): Promise<() => Promise> {
+ try {
if (!this.isConnected()) await this.connect()
if (_.isNil(this.#rxSink)) throw new Error('rxSink is undefined')
- ctx.timeout = ctx.timeout ?? READ_DEFAULT_TIMEOUT
- ctx.startedAt = Date.now()
- while (true) {
- if (!this.isConnected()) throw new Error('device disconnected')
- ctx.nowts = Date.now()
- if (ctx.nowts > ctx.startedAt + ctx.timeout) throw new Error(`readRespTimeout ${ctx.timeout}ms`)
- let buf = Buffer.concat(this._clearRxBufs())
- try {
- const sofIdx = buf.indexOf(START_OF_FRAME)
- if (sofIdx < 0) throw new Error('SOF not found')
- else if (sofIdx > 0) throw _.merge(new Error('ignore bytes before SOF'), { skip: sofIdx })
- // sof + sof lrc + cmd (2) + status (2) + data len (2) + head lrc + data + data lrc
- if (buf.length < 10) throw new Error('buf.length < 10')
- if (this._calcLrc(buf.subarray(2, 8)) !== buf[8]) throw _.merge(new Error('head lrc mismatch'), { skip: 1 })
- const lenFrame = buf.readUInt16BE(6) + 10
- if (buf.length < lenFrame) throw new Error('waiting for more data')
- if (this._calcLrc(buf.subarray(9, -1)) !== buf[buf.length - 1]) throw _.merge(new Error('data lrc mismatch'), { skip: 1 })
- ctx.resp = new ChameleonUltraFrame(buf.subarray(0, lenFrame))
- if (!_.isNil(cmd) && ctx.resp.cmd !== cmd) throw _.merge(new Error(`expect cmd=${cmd} but receive cmd=${ctx.resp.cmd}`), { skip: lenFrame })
- // resp is valid
- if (buf.length > lenFrame) this.#rxSink.bufs.unshift(buf.subarray(lenFrame))
- break
- } catch (err) {
- const skip = err.skip ?? 0
- if (skip > 0) {
- this.logger.respError(`readRespTimeout skip ${skip} byte(s), reason = ${err.message}`)
- buf = buf.subarray(skip)
+ if (_.isNil(args.timeout)) args.timeout = this.readDefaultTimeout
+ const respGenerator = new EventAsyncGenerator()
+ this.emitter.on('resp', respGenerator.onData)
+ this.emitter.once('disconnected', respGenerator.onClose)
+ let timeout: any | undefined
+ respGenerator.removeCallback = () => {
+ this.emitter.removeListener('resp', respGenerator.onData)
+ this.emitter.removeListener('disconnected', respGenerator.onClose)
+ if (!_.isNil(timeout)) clearTimeout(timeout)
+ }
+ return async () => {
+ timeout = setTimeout(() => {
+ respGenerator.onError(new Error(`read resp timeout (${args.timeout}ms)`))
+ }, args.timeout)
+ let resp: ChameleonUltraFrame | null = null
+ for await (const buf of respGenerator) {
+ const resp1 = new ChameleonUltraFrame(buf)
+ if (!_.isNil(args.cmd) && resp1.cmd !== args.cmd) continue
+ if (!(args.filter?.(resp1) ?? true)) continue
+ if (RespStatusFail.has(resp1.status)) {
+ this.#debug('respError', resp1.inspect)
+ const status = resp1.status
+ throw _.merge(new Error(RespStatusMsg.get(status)), { status, data: { resp } })
}
- this.#rxSink.bufs.unshift(buf)
+ this.#debug('resp', resp1.inspect)
+ resp = resp1
+ break
}
- await sleep(10)
- }
- if (RespStatusFail.has(ctx.resp.status)) {
- const status = ctx.resp.status
- this.logger.respError(ctx.resp.inspect)
- throw _.merge(new Error(RespStatusMsg.get(status)), { status, data: { resp: ctx.resp } })
+ if (_.isNil(resp)) throw new Error('device disconnected')
+ return resp
}
- this.logger.resp(ctx.resp.inspect)
- return ctx.resp
- }) as ChameleonUltraFrame
+ } catch (err) {
+ this.#debug('error', `${err.message}\n${err.stack}`)
+ throw err
+ }
}
/**
@@ -411,10 +364,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetAppVersion (): Promise<`${number}.${number}`> {
- this._clearRxBufs()
const cmd = Cmd.GET_APP_VERSION // cmd = 1000
- await this._writeCmd({ cmd })
- const { status, data } = await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ const { status, data } = await readResp()
if (status === RespStatus.HF_TAG_OK && data.readUInt16BE(0) === 0x0001) throw new Error('Unsupported protocol. Firmware update is required.')
return `${data[0]}.${data[1]}`
}
@@ -435,10 +388,10 @@ export class ChameleonUltra {
*/
async cmdChangeDeviceMode (mode: DeviceMode): Promise {
if (!isDeviceMode(mode)) throw new TypeError('Invalid device mode')
- this._clearRxBufs()
const cmd = Cmd.CHANGE_DEVICE_MODE // cmd = 1001
- await this._writeCmd({ cmd, data: Buffer.pack('!B', mode) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!B', mode) })
+ await readResp()
this.#deviceMode = mode
}
@@ -458,10 +411,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetDeviceMode (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_DEVICE_MODE // cmd = 1002
- await this._writeCmd({ cmd })
- const data = (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ const data = (await readResp()).data
this.#deviceMode = data[0]
return this.#deviceMode
}
@@ -491,10 +444,10 @@ export class ChameleonUltra {
*/
async cmdSlotSetActive (slot: Slot): Promise {
if (!isSlot(slot)) throw new TypeError('Invalid slot')
- this._clearRxBufs()
const cmd = Cmd.SET_ACTIVE_SLOT // cmd = 1003
- await this._writeCmd({ cmd, data: Buffer.pack('!B', slot) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!B', slot) })
+ await readResp()
}
/**
@@ -515,10 +468,10 @@ export class ChameleonUltra {
async cmdSlotChangeTagType (slot: Slot, tagType: TagType): Promise {
if (!isSlot(slot)) throw new TypeError('Invalid slot')
if (!isTagType(tagType)) throw new TypeError('Invalid tagType')
- this._clearRxBufs()
const cmd = Cmd.SET_SLOT_TAG_TYPE // cmd = 1004
- await this._writeCmd({ cmd, data: Buffer.pack('!BH', slot, tagType) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BH', slot, tagType) })
+ await readResp()
}
/**
@@ -539,10 +492,10 @@ export class ChameleonUltra {
async cmdSlotResetTagType (slot: Slot, tagType: TagType): Promise {
if (!isSlot(slot)) throw new TypeError('Invalid slot')
if (!isTagType(tagType)) throw new TypeError('Invalid tagType')
- this._clearRxBufs()
const cmd = Cmd.SET_SLOT_DATA_DEFAULT // cmd = 1005
- await this._writeCmd({ cmd, data: Buffer.pack('!BH', slot, tagType) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BH', slot, tagType) })
+ await readResp()
}
/**
@@ -564,10 +517,10 @@ export class ChameleonUltra {
if (!isSlot(slot)) throw new TypeError('Invalid slot')
if (!isValidFreqType(freq)) throw new TypeError('Invalid freq')
if (_.isNil(enable)) throw new TypeError('enable is required')
- this._clearRxBufs()
const cmd = Cmd.SET_SLOT_ENABLE // cmd = 1006
- await this._writeCmd({ cmd, data: Buffer.pack('!BB?', slot, freq, enable) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB?', slot, freq, enable) })
+ await readResp()
}
/**
@@ -592,10 +545,10 @@ export class ChameleonUltra {
if (!_.isString(name)) throw new TypeError('name should be a string')
const buf1 = Buffer.from(name)
if (!_.inRange(buf1.length, 1, 33)) throw new TypeError('byteLength of name should between 1 and 32')
- this._clearRxBufs()
const cmd = Cmd.SET_SLOT_TAG_NICK // cmd = 1007
- await this._writeCmd({ cmd, data: Buffer.pack(`!BB${buf1.length}s`, slot, freq, buf1) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack(`!BB${buf1.length}s`, slot, freq, buf1) })
+ await readResp()
}
/**
@@ -619,10 +572,10 @@ export class ChameleonUltra {
try {
if (!isSlot(slot)) throw new TypeError('Invalid slot')
if (!isValidFreqType(freq)) throw new TypeError('Invalid freq')
- this._clearRxBufs()
const cmd = Cmd.GET_SLOT_TAG_NICK // cmd = 1008
- await this._writeCmd({ cmd, data: Buffer.pack('!BB', slot, freq) })
- return (await this._readRespTimeout({ cmd }))?.data.toString('utf8')
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB', slot, freq) })
+ return (await readResp()).data.toString('utf8')
} catch (err) {
if (err.status === RespStatus.FLASH_READ_FAIL) return // slot name is empty
throw err
@@ -644,10 +597,10 @@ export class ChameleonUltra {
* ```
*/
async cmdSlotSaveSettings (): Promise {
- this._clearRxBufs()
const cmd = Cmd.SLOT_DATA_CONFIG_SAVE // cmd = 1009
- await this._writeCmd({ cmd })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ await readResp()
}
/**
@@ -663,9 +616,8 @@ export class ChameleonUltra {
* ```
*/
async cmdEnterBootloader (): Promise {
- this._clearRxBufs()
const cmd = Cmd.ENTER_BOOTLOADER // cmd = 1010
- await this._writeCmd({ cmd })
+ await this.#sendCmd({ cmd })
}
/**
@@ -682,10 +634,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetDeviceChipId (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_DEVICE_CHIP_ID // cmd = 1011
- await this._writeCmd({ cmd })
- const data = (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ const data = (await readResp()).data
return data.toString('hex')
}
@@ -703,10 +655,10 @@ export class ChameleonUltra {
* ```
*/
async cmdBleGetAddress (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_DEVICE_ADDRESS // cmd = 1012
- await this._writeCmd({ cmd })
- const data = (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ const data = (await readResp()).data
return (_.toUpper(data.toString('hex')).match(/.{2}/g) ?? []).join(':')
}
@@ -723,10 +675,10 @@ export class ChameleonUltra {
* ```
*/
async cmdSaveSettings (): Promise {
- this._clearRxBufs()
const cmd = Cmd.SAVE_SETTINGS // cmd = 1013
- await this._writeCmd({ cmd })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ await readResp()
}
/**
@@ -742,10 +694,10 @@ export class ChameleonUltra {
* ```
*/
async cmdResetSettings (): Promise {
- this._clearRxBufs()
const cmd = Cmd.RESET_SETTINGS // cmd = 1014
- await this._writeCmd({ cmd })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ await readResp()
}
/**
@@ -764,10 +716,10 @@ export class ChameleonUltra {
*/
async cmdSetAnimationMode (mode: AnimationMode): Promise {
if (!isAnimationMode(mode)) throw new TypeError('Invalid mode')
- this._clearRxBufs()
const cmd = Cmd.SET_ANIMATION_MODE // cmd = 1015
- await this._writeCmd({ cmd, data: Buffer.pack('!B', mode) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!B', mode) })
+ await readResp()
}
/**
@@ -786,10 +738,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetAnimationMode (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_ANIMATION_MODE // cmd = 1016
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0]
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0]
}
/**
@@ -806,11 +758,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetGitVersion (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_GIT_VERSION // cmd = 1017
- await this._writeCmd({ cmd })
- const data = (await this._readRespTimeout({ cmd }))?.data
- return data.toString('utf8')
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data.toString('utf8')
}
/**
@@ -829,10 +780,10 @@ export class ChameleonUltra {
* ```
*/
async cmdSlotGetActive (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_ACTIVE_SLOT // cmd = 1018
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0]
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0]
}
/**
@@ -862,10 +813,10 @@ export class ChameleonUltra {
* ```
*/
async cmdSlotGetInfo (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_SLOT_INFO // cmd = 1019
- await this._writeCmd({ cmd })
- return Decoder.SlotInfo.fromCmd1019((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return Decoder.SlotInfo.fromCmd1019((await readResp()).data)
}
/**
@@ -881,10 +832,10 @@ export class ChameleonUltra {
* ```
*/
async cmdWipeFds (): Promise {
- this._clearRxBufs()
const cmd = Cmd.WIPE_FDS // cmd = 1020
- await this._writeCmd({ cmd })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ await readResp()
}
/**
@@ -907,10 +858,10 @@ export class ChameleonUltra {
try {
if (!isSlot(slot)) throw new TypeError('Invalid slot')
if (!isValidFreqType(freq)) throw new TypeError('Invalid freq')
- this._clearRxBufs()
const cmd = Cmd.DELETE_SLOT_TAG_NICK // cmd = 1021
- await this._writeCmd({ cmd, data: Buffer.pack('!BB', slot, freq) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB', slot, freq) })
+ await readResp()
return true
} catch (err) {
if (err.status === RespStatus.FLASH_WRITE_FAIL) return false // slot name is empty
@@ -943,10 +894,10 @@ export class ChameleonUltra {
* ```
*/
async cmdSlotGetIsEnable (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_ENABLED_SLOTS // cmd = 1023
- await this._writeCmd({ cmd })
- return Decoder.SlotFreqIsEnable.fromCmd1023((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return Decoder.SlotFreqIsEnable.fromCmd1023((await readResp()).data)
}
/**
@@ -967,10 +918,10 @@ export class ChameleonUltra {
async cmdSlotDeleteFreqType (slot: Slot, freq: FreqType): Promise {
if (!isSlot(slot)) throw new TypeError('Invalid slot')
if (!isValidFreqType(freq)) throw new TypeError('Invalid freq')
- this._clearRxBufs()
const cmd = Cmd.DELETE_SLOT_SENSE_TYPE // cmd = 1024
- await this._writeCmd({ cmd, data: Buffer.pack('!BB', slot, freq) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB', slot, freq) })
+ await readResp()
}
/**
@@ -988,10 +939,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetBatteryInfo (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_BATTERY_INFO // cmd = 1025
- await this._writeCmd({ cmd })
- return Decoder.BatteryInfo.fromCmd1025((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return Decoder.BatteryInfo.fromCmd1025((await readResp()).data)
}
/**
@@ -1012,10 +963,10 @@ export class ChameleonUltra {
*/
async cmdGetButtonPressAction (btn: ButtonType): Promise {
if (!isButtonType(btn)) throw new TypeError('Invalid btn')
- this._clearRxBufs()
const cmd = Cmd.GET_BUTTON_PRESS_CONFIG // cmd = 1026
- await this._writeCmd({ cmd, data: Buffer.pack('!B', btn) })
- return (await this._readRespTimeout({ cmd }))?.data[0]
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!B', btn) })
+ return (await readResp()).data[0]
}
/**
@@ -1036,10 +987,10 @@ export class ChameleonUltra {
async cmdSetButtonPressAction (btn: ButtonType, action: ButtonAction): Promise {
if (!isButtonType(btn)) throw new TypeError('Invalid btn')
if (!isButtonAction(action)) throw new TypeError('Invalid action')
- this._clearRxBufs()
const cmd = Cmd.SET_BUTTON_PRESS_CONFIG // cmd = 1027
- await this._writeCmd({ cmd, data: Buffer.pack('!BB', btn, action) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB', btn, action) })
+ await readResp()
}
/**
@@ -1060,10 +1011,10 @@ export class ChameleonUltra {
*/
async cmdGetButtonLongPressAction (btn: ButtonType): Promise {
if (!isButtonType(btn)) throw new TypeError('Invalid btn')
- this._clearRxBufs()
const cmd = Cmd.GET_LONG_BUTTON_PRESS_CONFIG // cmd = 1028
- await this._writeCmd({ cmd, data: Buffer.pack('!B', btn) })
- return (await this._readRespTimeout({ cmd }))?.data[0]
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!B', btn) })
+ return (await readResp()).data[0]
}
/**
@@ -1084,10 +1035,10 @@ export class ChameleonUltra {
async cmdSetButtonLongPressAction (btn: ButtonType, action: ButtonAction): Promise {
if (!isButtonType(btn)) throw new TypeError('Invalid btn')
if (!isButtonAction(action)) throw new TypeError('Invalid action')
- this._clearRxBufs()
const cmd = Cmd.SET_LONG_BUTTON_PRESS_CONFIG // cmd = 1029
- await this._writeCmd({ cmd, data: Buffer.pack('!BB', btn, action) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB', btn, action) })
+ await readResp()
}
/**
@@ -1105,10 +1056,10 @@ export class ChameleonUltra {
*/
async cmdBleSetPairingKey (key: string): Promise {
if (!_.isString(key) || !/^\d{6}$/.test(key)) throw new TypeError('Invalid key, must be 6 digits')
- this._clearRxBufs()
const cmd = Cmd.SET_BLE_PAIRING_KEY // cmd = 1030
- await this._writeCmd({ cmd, data: Buffer.from(key) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.from(key) })
+ await readResp()
}
/**
@@ -1125,10 +1076,10 @@ export class ChameleonUltra {
* ```
*/
async cmdBleGetPairingKey (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_BLE_PAIRING_KEY // cmd = 1031
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data.toString('utf8')
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data.toString('utf8')
}
/**
@@ -1144,10 +1095,10 @@ export class ChameleonUltra {
* ```
*/
async cmdBleDeleteAllBonds (): Promise {
- this._clearRxBufs()
const cmd = Cmd.DELETE_ALL_BLE_BONDS // cmd = 1032
- await this._writeCmd({ cmd })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ await readResp()
}
/**
@@ -1166,11 +1117,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetDeviceModel (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_DEVICE_MODEL // cmd = 1033
- await this._writeCmd({ cmd })
- const data = (await this._readRespTimeout({ cmd }))?.data
- return data[0]
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0]
}
/**
@@ -1198,10 +1148,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetDeviceSettings (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_DEVICE_SETTINGS // cmd = 1034
- await this._writeCmd({ cmd })
- return Decoder.DeviceSettings.fromCmd1034((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return Decoder.DeviceSettings.fromCmd1034((await readResp()).data)
}
/**
@@ -1219,10 +1169,10 @@ export class ChameleonUltra {
* ```
*/
async cmdGetSupportedCmds (): Promise> {
- this._clearRxBufs()
const cmd = Cmd.GET_DEVICE_CAPABILITIES // cmd = 1035
- await this._writeCmd({ cmd })
- const data = (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ const data = (await readResp()).data
const cmds = new Set()
for (let i = 0; i < data.length; i += 2) cmds.add(data.readUInt16BE(i))
return cmds
@@ -1260,10 +1210,10 @@ export class ChameleonUltra {
* ```
*/
async cmdBleGetPairingMode (): Promise {
- this._clearRxBufs()
const cmd = Cmd.GET_BLE_PAIRING_ENABLE // cmd = 1036
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0] === 1
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0] === 1
}
/**
@@ -1281,10 +1231,10 @@ export class ChameleonUltra {
*/
async cmdBleSetPairingMode (enable: boolean | number): Promise {
if (_.isNil(enable)) throw new TypeError('enable is required')
- this._clearRxBufs()
const cmd = Cmd.SET_BLE_PAIRING_ENABLE // cmd = 1037
- await this._writeCmd({ cmd, data: Buffer.pack('!?', enable) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!?', enable) })
+ await readResp()
}
/**
@@ -1305,10 +1255,10 @@ export class ChameleonUltra {
*/
async cmdHf14aScan (): Promise {
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.HF14A_SCAN // cmd = 2000
- await this._writeCmd({ cmd })
- return Decoder.Hf14aAntiColl.fromCmd2000((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return Decoder.Hf14aAntiColl.fromCmd2000((await readResp()).data)
}
/**
@@ -1327,10 +1277,10 @@ export class ChameleonUltra {
async cmdMf1IsSupport (): Promise {
try {
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_DETECT_SUPPORT // cmd = 2001
- await this._writeCmd({ cmd })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ await readResp()
return true
} catch (err) {
if (err.status === RespStatus.HF_ERR_STAT) return false
@@ -1354,10 +1304,10 @@ export class ChameleonUltra {
*/
async cmdMf1TestPrngType (): Promise {
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_DETECT_NT_LEVEL // cmd = 2002
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0]
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0]
}
/**
@@ -1409,10 +1359,10 @@ export class ChameleonUltra {
if (!isMf1BlockNo(target.block)) throw new TypeError('Invalid target.block')
if (!isMf1KeyType(target.keyType)) throw new TypeError('Invalid target.keyType')
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_STATIC_NESTED_ACQUIRE // cmd = 2003
- await this._writeCmd({ cmd, data: Buffer.pack('!BB6sBB', known.keyType, known.block, known.key, target.keyType, target.block) })
- return Decoder.Mf1AcquireStaticNestedRes.fromCmd2003((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB6sBB', known.keyType, known.block, known.key, target.keyType, target.block) })
+ return Decoder.Mf1AcquireStaticNestedRes.fromCmd2003((await readResp()).data)
}
/**
@@ -1491,10 +1441,10 @@ export class ChameleonUltra {
if (_.isNil(isFirst)) throw new TypeError('Invalid isFirst')
if (!_.isSafeInteger(syncMax)) throw new TypeError('Invalid syncMax')
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_DARKSIDE_ACQUIRE // cmd = 2004
- await this._writeCmd({ cmd, data: Buffer.pack('!BB?B', keyType, block, isFirst, syncMax) })
- return Decoder.Mf1DarksideRes.fromCmd2004((await this._readRespTimeout({ cmd, timeout: syncMax * 1e4 }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd, timeout: syncMax * 1e4 })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB?B', keyType, block, isFirst, syncMax) })
+ return Decoder.Mf1DarksideRes.fromCmd2004((await readResp()).data)
}
/**
@@ -1541,10 +1491,10 @@ export class ChameleonUltra {
async cmdMf1TestNtDistance (known: { block: number, key: Buffer, keyType: Mf1KeyType }): Promise {
validateMf1BlockKey(known.block, known.keyType, known.key, 'known.')
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_DETECT_NT_DIST // cmd = 2005
- await this._writeCmd({ cmd, data: Buffer.pack('!BB6s', known.keyType, known.block, known.key) })
- return Decoder.Mf1NtDistanceRes.fromCmd2005((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB6s', known.keyType, known.block, known.key) })
+ return Decoder.Mf1NtDistanceRes.fromCmd2005((await readResp()).data)
}
/**
@@ -1599,10 +1549,10 @@ export class ChameleonUltra {
if (!_.isSafeInteger(target.block)) throw new TypeError('Invalid target.block')
if (!isMf1KeyType(target.keyType)) throw new TypeError('Invalid target.keyType')
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_NESTED_ACQUIRE // cmd = 2006
- await this._writeCmd({ cmd, data: Buffer.pack('!BB6sBB', known.keyType, known.block, known.key, target.keyType, target.block) })
- return Decoder.Mf1NestedRes.fromCmd2006((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB6sBB', known.keyType, known.block, known.key, target.keyType, target.block) })
+ return Decoder.Mf1NestedRes.fromCmd2006((await readResp()).data)
}
/**
@@ -1633,10 +1583,10 @@ export class ChameleonUltra {
try {
validateMf1BlockKey(block, keyType, key)
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_AUTH_ONE_KEY_BLOCK // cmd = 2007
- await this._writeCmd({ cmd, data: Buffer.pack('!BB6s', keyType, block, key) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB6s', keyType, block, key) })
+ await readResp()
return true
} catch (err) {
if (err.status === RespStatus.MF_ERR_AUTH) return false
@@ -1672,10 +1622,10 @@ export class ChameleonUltra {
const { block, keyType, key } = opts
validateMf1BlockKey(block, keyType, key)
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_READ_ONE_BLOCK // cmd = 2008
- await this._writeCmd({ cmd, data: Buffer.pack('!BB6s', keyType, block, key) })
- return (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB6s', keyType, block, key) })
+ return (await readResp()).data
}
/**
@@ -1713,10 +1663,10 @@ export class ChameleonUltra {
validateMf1BlockKey(block, keyType, key)
if (!Buffer.isBuffer(data) || data.length !== 16) throw new TypeError('data should be a Buffer with length 16')
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_WRITE_ONE_BLOCK // cmd = 2009
- await this._writeCmd({ cmd, data: Buffer.pack('!BB6s16s', keyType, block, key, data) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB6s16s', keyType, block, key, data) })
+ await readResp()
}
/**
@@ -1759,8 +1709,8 @@ export class ChameleonUltra {
* @param opts.data - The data to be send. If `appendCrc` is `true`, the maximum length of data is `62`, otherwise is `64`.
* @param opts.dataBitLength - Number of bits to send. Useful for send partial byte. `dataBitLength` is incompatible with `appendCrc`.
* @param opts.keepRfField - Set `true` to keep the RF field active after sending.
- * @param opts.waitResponse - Default value is `true`. Set `false` to skip reading tag response.
- * @param opts.timeout - Default value is `1000 ms`. Maximum timeout for reading tag response in ms while `waitResponse` is `true`.
+ * @param opts.readResponse - Default value is `true`. Set `false` to skip reading tag response.
+ * @param opts.timeout - Default value is `1000 ms`. Maximum timeout for reading tag response in ms while `readResponse` is `true`.
* @returns The response from tag.
* @group Reader Related
*/
@@ -1806,10 +1756,10 @@ export class ChameleonUltra {
] as Array<[number, boolean]>) buf1.writeBitMSB(val, bitOffset)
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.HF14A_RAW // cmd = 2010
- await this._writeCmd({ cmd, data: buf1 })
- return (await this._readRespTimeout({ cmd, timeout: READ_DEFAULT_TIMEOUT + timeout }))?.data
+ const readResp = await this.#createReadRespFn({ cmd, timeout: READ_DEFAULT_TIMEOUT + timeout })
+ await this.#sendCmd({ cmd, data: buf1 })
+ return (await readResp()).data
}
/**
@@ -1860,11 +1810,11 @@ export class ChameleonUltra {
if (!isMf1VblockOperator(operator)) throw new TypeError('Invalid operator')
if (!_.isSafeInteger(operand)) throw new TypeError('Invalid operand')
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_MANIPULATE_VALUE_BLOCK // cmd = 2011
const data = Buffer.pack('!BB6sBiBB6s', src.keyType, src.block, src.key, operator, operand, dst.keyType, dst.block, dst.key)
- await this._writeCmd({ cmd, data })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data })
+ await readResp()
}
/**
@@ -2000,12 +1950,12 @@ export class ChameleonUltra {
if (bitsCnt < 1) return null
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.MF1_CHECK_KEYS_OF_SECTORS // cmd = 2012
const data = Buffer.concat([mask, ...keys])
const timeout = READ_DEFAULT_TIMEOUT + bitsCnt * (keys.length + 1) * 100
- await this._writeCmd({ cmd, data })
- return Decoder.Mf1CheckKeysOfSectorsRes.fromCmd2012((await this._readRespTimeout({ cmd, timeout }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd, timeout })
+ await this.#sendCmd({ cmd, data })
+ return Decoder.Mf1CheckKeysOfSectorsRes.fromCmd2012((await readResp()).data)
}
/**
@@ -2024,10 +1974,10 @@ export class ChameleonUltra {
*/
async cmdEm410xScan (): Promise {
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.EM410X_SCAN // cmd = 3000
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data
}
/**
@@ -2047,12 +1997,12 @@ export class ChameleonUltra {
async cmdEm410xWriteToT55xx (id: Buffer): Promise {
if (!Buffer.isBuffer(id) || id.length !== 5) throw new TypeError('id should be a Buffer with length 5')
await this.assureDeviceMode(DeviceMode.READER)
- this._clearRxBufs()
const cmd = Cmd.EM410X_WRITE_TO_T55XX // cmd = 3001
const oldKeys = [0x51243648, 0x19920427]
const data = Buffer.pack(`!5sI${oldKeys.length}I`, id, 0x20206666, ...oldKeys)
- await this._writeCmd({ cmd, data })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data })
+ await readResp()
}
/**
@@ -2073,10 +2023,10 @@ export class ChameleonUltra {
async cmdMf1EmuWriteBlock (offset: number, data: Buffer): Promise {
if (!_.isSafeInteger(offset)) throw new TypeError('Invalid offset')
if (!Buffer.isBuffer(data) || data.length % 16 !== 0) throw new TypeError('data should be a Buffer with length be multiples of 16')
- this._clearRxBufs()
const cmd = Cmd.MF1_WRITE_EMU_BLOCK_DATA // cmd = 4000
- await this._writeCmd({ cmd, data: Buffer.pack(`!B${data.length}s`, offset, data) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack(`!B${data.length}s`, offset, data) })
+ await readResp()
}
/**
@@ -2111,10 +2061,10 @@ export class ChameleonUltra {
if (!Buffer.isBuffer(atqa) || atqa.length !== 2) throw new TypeError('atqa should be a Buffer with length 2')
if (!Buffer.isBuffer(sak) || sak.length !== 1) throw new TypeError('sak should be a Buffer with length 1')
if (!Buffer.isBuffer(ats)) throw new TypeError('ats should be a Buffer')
- this._clearRxBufs()
const cmd = Cmd.HF14A_SET_ANTI_COLL_DATA // cmd = 4001
- await this._writeCmd({ cmd, data: Buffer.pack(`!${uid.length + 1}p2ss${ats.length + 1}p`, uid, atqa, sak, ats) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack(`!${uid.length + 1}p2ss${ats.length + 1}p`, uid, atqa, sak, ats) })
+ await readResp()
}
/**
@@ -2132,10 +2082,10 @@ export class ChameleonUltra {
*/
async cmdMf1SetDetectionEnable (enable: boolean | number): Promise {
if (_.isNil(enable)) throw new TypeError('enable is required')
- this._clearRxBufs()
const cmd = Cmd.MF1_SET_DETECTION_ENABLE // cmd = 4004
- await this._writeCmd({ cmd, data: Buffer.pack('!?', enable) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!?', enable) })
+ await readResp()
}
/**
@@ -2152,10 +2102,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1GetDetectionCount (): Promise {
- this._clearRxBufs()
const cmd = Cmd.MF1_GET_DETECTION_COUNT // cmd = 4005
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data.readUInt32BE()
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data.readUInt32BE()
}
/**
@@ -2186,10 +2136,10 @@ export class ChameleonUltra {
*/
async cmdMf1GetDetectionLogs (offset: number = 0): Promise {
if (!_.isSafeInteger(offset)) throw new TypeError('Invalid offset')
- this._clearRxBufs()
const cmd = Cmd.MF1_GET_DETECTION_LOG // cmd = 4006
- await this._writeCmd({ cmd, data: Buffer.pack('!I', offset) })
- return Decoder.Mf1DetectionLog.fromCmd4006((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!I', offset) })
+ return Decoder.Mf1DetectionLog.fromCmd4006((await readResp()).data)
}
/**
@@ -2206,10 +2156,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1GetDetectionEnable (): Promise {
- this._clearRxBufs()
const cmd = Cmd.MF1_GET_DETECTION_ENABLE // cmd = 4007
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0] === 1
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0] === 1
}
/**
@@ -2229,10 +2179,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1EmuReadBlock (offset: number = 0, length: number = 1): Promise {
- this._clearRxBufs()
const cmd = Cmd.MF1_READ_EMU_BLOCK_DATA // cmd = 4008
- await this._writeCmd({ cmd, data: Buffer.pack('!BB', offset, length) })
- return (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!BB', offset, length) })
+ return (await readResp()).data
}
/**
@@ -2259,10 +2209,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1GetEmuSettings (): Promise {
- this._clearRxBufs()
const cmd = Cmd.MF1_GET_EMULATOR_CONFIG // cmd = 4009
- await this._writeCmd({ cmd })
- return Decoder.Mf1EmuSettings.fromCmd4009((await this._readRespTimeout({ cmd }))?.data)
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return Decoder.Mf1EmuSettings.fromCmd4009((await readResp()).data)
}
/**
@@ -2279,10 +2229,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1GetGen1aMode (): Promise {
- this._clearRxBufs()
const cmd = Cmd.MF1_GET_GEN1A_MODE // cmd = 4010
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0] === 1
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0] === 1
}
/**
@@ -2300,10 +2250,10 @@ export class ChameleonUltra {
*/
async cmdMf1SetGen1aMode (enable: boolean | number): Promise {
if (_.isNil(enable)) throw new TypeError('enable is required')
- this._clearRxBufs()
const cmd = Cmd.MF1_SET_GEN1A_MODE // cmd = 4011
- await this._writeCmd({ cmd, data: Buffer.pack('!?', enable) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!?', enable) })
+ await readResp()
}
/**
@@ -2320,10 +2270,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1GetGen2Mode (): Promise {
- this._clearRxBufs()
const cmd = Cmd.MF1_GET_GEN2_MODE // cmd = 4012
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0] === 1
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0] === 1
}
/**
@@ -2341,10 +2291,10 @@ export class ChameleonUltra {
*/
async cmdMf1SetGen2Mode (enable: boolean | number): Promise {
if (_.isNil(enable)) throw new TypeError('enable is required')
- this._clearRxBufs()
const cmd = Cmd.MF1_SET_GEN2_MODE // cmd = 4013
- await this._writeCmd({ cmd, data: Buffer.pack('!?', enable) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!?', enable) })
+ await readResp()
}
/**
@@ -2361,10 +2311,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1GetAntiCollMode (): Promise {
- this._clearRxBufs()
const cmd = Cmd.HF14A_GET_BLOCK_ANTI_COLL_MODE // cmd = 4014
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0] === 1
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0] === 1
}
/**
@@ -2382,10 +2332,10 @@ export class ChameleonUltra {
*/
async cmdMf1SetAntiCollMode (enable: boolean | number): Promise {
if (_.isNil(enable)) throw new TypeError('enable is required')
- this._clearRxBufs()
const cmd = Cmd.HF14A_SET_BLOCK_ANTI_COLL_MODE // cmd = 4015
- await this._writeCmd({ cmd, data: Buffer.pack('!?', enable) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!?', enable) })
+ await readResp()
}
/**
@@ -2402,10 +2352,10 @@ export class ChameleonUltra {
* ```
*/
async cmdMf1GetWriteMode (): Promise {
- this._clearRxBufs()
const cmd = Cmd.MF1_GET_WRITE_MODE // cmd = 4016
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data[0]
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data[0]
}
/**
@@ -2424,10 +2374,10 @@ export class ChameleonUltra {
*/
async cmdMf1SetWriteMode (mode: Mf1EmuWriteMode): Promise {
if (!isMf1EmuWriteMode(mode)) throw new TypeError('Invalid mode')
- this._clearRxBufs()
const cmd = Cmd.MF1_SET_WRITE_MODE // cmd = 4017
- await this._writeCmd({ cmd, data: Buffer.pack('!B', mode) })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: Buffer.pack('!B', mode) })
+ await readResp()
}
/**
@@ -2451,10 +2401,10 @@ export class ChameleonUltra {
* ```
*/
async cmdHf14aGetAntiCollData (): Promise {
- this._clearRxBufs()
const cmd = Cmd.HF14A_GET_ANTI_COLL_DATA // cmd = 4018
- await this._writeCmd({ cmd })
- const data = (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ const data = (await readResp()).data
return data.length > 0 ? Decoder.Hf14aAntiColl.fromBuffer(data) : null
}
@@ -2474,10 +2424,10 @@ export class ChameleonUltra {
*/
async cmdEm410xSetEmuId (id: Buffer): Promise {
if (!Buffer.isBuffer(id) || id.length !== 5) throw new TypeError('id should be a Buffer with length 5')
- this._clearRxBufs()
const cmd = Cmd.EM410X_SET_EMU_ID // cmd = 5000
- await this._writeCmd({ cmd, data: id })
- await this._readRespTimeout({ cmd })
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd, data: id })
+ await readResp()
}
/**
@@ -2495,10 +2445,10 @@ export class ChameleonUltra {
* ```
*/
async cmdEm410xGetEmuId (): Promise {
- this._clearRxBufs()
const cmd = Cmd.EM410X_GET_EMU_ID // cmd = 5001
- await this._writeCmd({ cmd })
- return (await this._readRespTimeout({ cmd }))?.data
+ const readResp = await this.#createReadRespFn({ cmd })
+ await this.#sendCmd({ cmd })
+ return (await readResp()).data
}
/**
@@ -2807,7 +2757,7 @@ export class ChameleonUltra {
break
} catch (err) {
if (!this.isConnected()) throw err
- this.logger.core(`Failed to read block ${sector * 4 + i} with ${Mf1KeyType[keyType]} = ${key.toString('hex')}`)
+ this.#debug('mf1', `Failed to read block ${sector * 4 + i} with ${Mf1KeyType[keyType]} = ${key.toString('hex')}`)
}
}
}
@@ -2858,7 +2808,7 @@ export class ChameleonUltra {
break
} catch (err) {
if (!this.isConnected()) throw err
- this.logger.core(`Failed to write block ${sector * 4 + i} with ${Mf1KeyType[keyType]} = ${key.toString('hex')}`)
+ this.#debug('mf1', `Failed to write block ${sector * 4 + i} with ${Mf1KeyType[keyType]} = ${key.toString('hex')}`)
}
}
}
@@ -2891,12 +2841,6 @@ export class ChameleonUltra {
}
}
-/**
- * @internal
- * @group Internal
- */
-export type Logger = Debugger | ((...args: any[]) => void)
-
const RespStatusMsg = new Map([
[RespStatus.HF_TAG_OK, 'HF tag operation succeeded'],
[RespStatus.HF_TAG_NOT_FOUND, 'HF tag not found error'],
@@ -2947,17 +2891,65 @@ export interface ChameleonSerialPort {
}
class ChameleonRxSink implements UnderlyingSink {
+ #closed: boolean = false
+ #started: boolean = false
+ abortController: AbortController = new AbortController()
bufs: Buffer[] = []
- controller: AbortController
+ readonly #ultra: ChameleonUltra
+
+ constructor (ultra: ChameleonUltra) {
+ this.#ultra = ultra
+ }
- constructor () {
- this.controller = new AbortController()
+ start (controller: WritableStreamDefaultController): void {
+ if (this.#closed) throw new Error('rxSink is closed')
+ if (this.#started) throw new Error('rxSink is already started')
+ this.#ultra.emitter.emit('connected', new Date())
+ this.#started = true
}
- get signal (): AbortSignal { return this.controller.signal }
+ write (chunk: Buffer, controller: WritableStreamDefaultController): void {
+ if (!this.#started || this.#closed) return
+ if (!Buffer.isBuffer(chunk)) chunk = Buffer.fromView(chunk)
+ this.bufs.push(chunk)
+ let buf = Buffer.concat(this.bufs.splice(0, this.bufs.length))
+ try {
+ while (buf.length > 0) {
+ const sofIdx = buf.indexOf(START_OF_FRAME)
+ if (sofIdx < 0) break // end, SOF not found
+ else if (sofIdx > 0) buf = buf.subarray(sofIdx) // ignore bytes before SOF
+ // sof + sof lrc + cmd (2) + status (2) + data len (2) + head lrc + data + data lrc
+ if (buf.length < 10) break // end, buf.length < 10
+ if (bufLrc(buf.subarray(2, 8)) !== buf[8]) {
+ buf = buf.subarray(1) // skip 1 byte, head lrc mismatch
+ continue
+ }
+ const lenFrame = buf.readUInt16BE(6) + 10
+ if (buf.length < lenFrame) break // end, wait for more data
+ if (bufLrc(buf.subarray(9, lenFrame - 1)) !== buf[lenFrame - 1]) {
+ buf = buf.subarray(1) // skip 1 byte, data lrc mismatch
+ continue
+ }
+ this.#ultra.emitter.emit('resp', buf.slice(0, lenFrame))
+ buf = buf.subarray(lenFrame)
+ }
+ } finally {
+ if (buf.length > 0) this.bufs.push(buf)
+ }
+ }
+
+ close (): void {
+ if (this.#closed) return
+ this.#closed = true
+ this.abortController.abort()
+ this.#ultra.emitter.emit('disconnected', new Date())
+ }
- write (chunk: Buffer): void {
- this.bufs.push(Buffer.from(chunk))
+ abort (reason: any): void {
+ if (this.#closed) return
+ this.#closed = true
+ this.abortController.abort()
+ this.#ultra.emitter.emit('disconnected', new Date(), reason)
}
}
@@ -3022,4 +3014,10 @@ const NxpTypeBySak = new Map([
[0x38, 'SmartMX with MIFARE Classic 4K'],
])
+function bufLrc (buf: Buffer): number {
+ let sum = 0
+ for (const u8 of buf) sum += u8
+ return 0x100 - sum & 0xFF
+}
+
export { Decoder as ResponseDecoder }
diff --git a/src/EventAsyncGenerator.test.ts b/src/EventAsyncGenerator.test.ts
new file mode 100644
index 0000000..cbaba9d
--- /dev/null
+++ b/src/EventAsyncGenerator.test.ts
@@ -0,0 +1,215 @@
+import { EventEmitter } from 'events'
+import { EventAsyncGenerator } from './EventAsyncGenerator'
+
+const sleep = async (t: number): Promise => new Promise(resolve => setTimeout(resolve, t))
+
+describe('EventIterable', () => {
+ test('should await immediate onData value', async () => {
+ const it = new EventAsyncGenerator()
+ it.onData(1)
+ expect(await it.next()).toEqual({ value: 1, done: false })
+ })
+
+ test('should await dalayed onData value', async () => {
+ const it = new EventAsyncGenerator()
+ void sleep(10).then(() => { it.onData(1) }) // no wait
+ expect(await it.next()).toEqual({ value: 1, done: false })
+ })
+
+ test('should await immediate end', async () => {
+ const it = new EventAsyncGenerator()
+ it.onClose()
+ expect(await it.next()).toEqual({ value: undefined, done: true })
+ })
+
+ test('should await delayed end', async () => {
+ const it = new EventAsyncGenerator()
+ void sleep(10).then(async () => { it.onClose() }) // no wait
+ expect(await it.next()).toEqual({ value: undefined, done: true })
+ })
+
+ test('should await immediate error', async () => {
+ expect.assertions(1)
+
+ try {
+ const it = new EventAsyncGenerator()
+ it.onError(new Error())
+ await it.next()
+ } catch (err) {
+ expect(err).toBeInstanceOf(Error)
+ }
+ })
+
+ test('should await delayed error', async () => {
+ expect.assertions(1)
+
+ try {
+ const it = new EventAsyncGenerator()
+ void sleep(10).then(async () => { it.onError(new Error()) }) // no wait
+ await it.next()
+ } catch (err) {
+ expect(err).toBeInstanceOf(Error)
+ }
+ })
+
+ test('does not yield new items if return has been called', async () => {
+ const it = new EventAsyncGenerator()
+ void it.return(undefined)
+ expect(await it.next()).toEqual({ value: undefined, done: true })
+ })
+
+ test('does not queue for new items if return has been called', async () => {
+ const it = new EventAsyncGenerator()
+ it.onData(1)
+ expect(await it.next()).toEqual({ value: 1, done: false })
+ expect(await it.return(undefined)).toEqual({ value: undefined, done: true })
+ it.onData(2)
+ expect(await it.next()).toEqual({ value: undefined, done: true })
+ })
+
+ test('should call remove handler once with no arguments on immediate end', async () => {
+ const it = new EventAsyncGenerator()
+ it.removeCallback = jest.fn()
+ it.onClose()
+ expect(it.removeCallback).toHaveBeenCalledTimes(1)
+ expect(it.removeCallback).toHaveBeenCalledWith()
+ })
+
+ test('should call remove handler once with no arguments on delayed end', async () => {
+ const it = new EventAsyncGenerator()
+ it.removeCallback = jest.fn()
+ void sleep(10).then(() => { it.onClose() }) // no wait
+ await sleep(20)
+ expect(it.removeCallback).toHaveBeenCalledTimes(1)
+ expect(it.removeCallback).toHaveBeenCalledWith()
+ })
+
+ test('should call remove handler on immediate return', async () => {
+ const it = new EventAsyncGenerator()
+ it.removeCallback = jest.fn()
+ await it.return(undefined)
+ expect(it.removeCallback).toHaveBeenCalledTimes(1)
+ expect(it.removeCallback).toHaveBeenCalledWith()
+ })
+
+ test('should call remove handler on delayed return', async () => {
+ const it = new EventAsyncGenerator()
+ it.removeCallback = jest.fn()
+ void sleep(10).then(async () => { await it.return(undefined) }) // no wait
+ await sleep(20)
+ expect(it.removeCallback).toHaveBeenCalledTimes(1)
+ expect(it.removeCallback).toHaveBeenCalledWith()
+ })
+
+ test('should call remove handler on immediate error', async () => {
+ expect.hasAssertions()
+ const it = new EventAsyncGenerator()
+ try {
+ it.removeCallback = jest.fn()
+ it.onError(new Error())
+ await it.next()
+ } catch (err) {
+ expect(err).toBeInstanceOf(Error)
+ expect(it.removeCallback).toHaveBeenCalledTimes(1)
+ expect(it.removeCallback).toHaveBeenCalledWith()
+ }
+ })
+
+ test('should call remove handler on delayed error', async () => {
+ expect.hasAssertions()
+ const it = new EventAsyncGenerator()
+ try {
+ it.removeCallback = jest.fn()
+ void sleep(10).then(() => { it.onError(new Error()) }) // no wait
+ await it.next()
+ } catch (err) {
+ expect(err).toBeInstanceOf(Error)
+ expect(it.removeCallback).toHaveBeenCalledTimes(1)
+ expect(it.removeCallback).toHaveBeenCalledWith()
+ }
+ })
+
+ test('should buffer iterator calls when the queue is empty', async () => {
+ const event = new EventEmitter()
+ const it = new EventAsyncGenerator()
+ event.on('data', it.onData)
+ it.removeCallback = () => {
+ event.removeListener('data', it.onData)
+ }
+
+ const reqs = Promise.all([it.next(), it.next()])
+ event.emit('data', 1)
+ event.emit('data', 2)
+
+ const actual = await reqs
+ expect(actual).toMatchObject([
+ { value: 1, done: false },
+ { value: 2, done: false },
+ ])
+ })
+
+ test('should broadcast all reqs when event emitter closes', async () => {
+ const event = new EventEmitter()
+ const it = new EventAsyncGenerator()
+ event.on('data', it.onData)
+ event.on('close', it.onClose)
+ it.removeCallback = () => {
+ event.removeListener('data', it.onData)
+ event.removeListener('close', it.onClose)
+ }
+
+ event.emit('data', 1)
+ event.emit('data', 2)
+ event.emit('close')
+ event.emit('data', 3)
+
+ const actual = await Promise.all([it.next(), it.next(), it.next(), it.next()])
+ expect(actual).toMatchObject([
+ { value: 1, done: false },
+ { value: 2, done: false },
+ { value: undefined, done: true },
+ { value: undefined, done: true },
+ ])
+ })
+
+ test('it should buffer iterator calls and yield undefined after return', async () => {
+ const event = new EventEmitter()
+ const it = new EventAsyncGenerator()
+ event.on('data', it.onData)
+ it.removeCallback = () => {
+ event.removeListener('data', it.onData)
+ }
+
+ const reqs = Promise.all([it.next(), it.return(undefined), it.next()])
+ void sleep(10).then(() => { event.emit('data', 1) }) // no wait
+ void sleep(20).then(() => { event.emit('data', 2) }) // no wait
+ void sleep(30).then(() => { event.emit('data', 3) }) // no wait
+
+ const actual = await reqs
+ expect(actual).toMatchObject([
+ { value: 1, done: false },
+ { value: undefined, done: true },
+ { value: undefined, done: true },
+ ])
+ })
+
+ test('it should buffer values and yield undefined after return', async () => {
+ const event = new EventEmitter()
+ const it = new EventAsyncGenerator()
+ event.on('data', it.onData)
+ it.removeCallback = () => {
+ event.removeListener('data', it.onData)
+ }
+
+ event.emit('data', 1)
+ event.emit('data', 2)
+ event.emit('data', 3)
+
+ const actual = await Promise.all([it.next(), it.return(undefined), it.next()])
+ expect(actual).toMatchObject([
+ { value: 1, done: false },
+ { value: undefined, done: true },
+ { value: undefined, done: true },
+ ])
+ })
+})
diff --git a/src/EventAsyncGenerator.ts b/src/EventAsyncGenerator.ts
new file mode 100644
index 0000000..5d89260
--- /dev/null
+++ b/src/EventAsyncGenerator.ts
@@ -0,0 +1,90 @@
+const symbolClose = Symbol.for('EventAsyncGenerator.close')
+
+export class EventAsyncGenerator implements AsyncGenerator {
+ #isFinally = false
+ #pullPromise: Resolvable | null = null
+ readonly #it: AsyncGenerator
+ readonly #queue: Array = []
+
+ onClose: () => void
+ onData: (value: T) => void
+ onError: (err: Error) => void
+ removeCallback?: () => void | Promise
+
+ constructor (init?: (me: EventAsyncGenerator) => void | Promise) {
+ const me = this // eslint-disable-line @typescript-eslint/no-this-alias
+ this.onData = (value: T) => {
+ if (this.#pullPromise !== null) this.#pullPromise.resolve?.(value)
+ else this.#queue.push(value)
+ }
+ this.onClose = () => {
+ if (this.#pullPromise !== null) this.#pullPromise.resolve?.(symbolClose)
+ else this.#queue.push(symbolClose)
+ void this.finally()
+ }
+ this.onError = (err: Error) => {
+ if (this.#pullPromise !== null) this.#pullPromise.reject?.(err)
+ else this.#queue.push(err)
+ void this.finally()
+ }
+ this.#it = (async function * () {
+ try {
+ await init?.(me)
+ while (true) {
+ let valueOrErr: T | typeof symbolClose | Error
+ if (me.#queue.length > 0) valueOrErr = me.#queue.shift() as T | typeof symbolClose | Error
+ else {
+ me.#pullPromise = createResolvable()
+ valueOrErr = await me.#pullPromise.catch(err => err)
+ me.#pullPromise = null
+ }
+ if (valueOrErr === symbolClose) return
+ if (valueOrErr instanceof Error) throw valueOrErr
+ yield valueOrErr
+ }
+ } finally {
+ await me.finally()
+ }
+ })() as AsyncGenerator
+ }
+
+ async next (...args: [] | [TNext]): Promise> {
+ return await this.#it.next(...args)
+ }
+
+ async return (value: TReturn | PromiseLike): Promise> {
+ const result = await this.#it.return(value)
+ await this.finally()
+ return result
+ }
+
+ async throw (err: Error): Promise> {
+ const result = await this.#it.throw(err)
+ await this.finally()
+ return result
+ }
+
+ async finally (): Promise {
+ if (this.#isFinally) return
+ this.#isFinally = true
+ await this.removeCallback?.()
+ }
+
+ [Symbol.asyncIterator] (): this {
+ return this
+ }
+}
+
+type Resolvable = Promise & {
+ resolve: (t: T) => void
+ reject: (err: Error) => void
+}
+
+function createResolvable (): Resolvable {
+ let resolve, reject
+ const resolvable = new Promise((...args) => {
+ ;[resolve, reject] = args
+ }) as Resolvable
+ Object.assign(resolvable, { resolve, reject })
+ return resolvable
+}
diff --git a/src/EventEmitter.test.ts b/src/EventEmitter.test.ts
new file mode 100644
index 0000000..b19340d
--- /dev/null
+++ b/src/EventEmitter.test.ts
@@ -0,0 +1,415 @@
+import { EventEmitter } from './EventEmitter'
+// import { EventEmitter } from 'node:events'
+
+describe('Event: error', () => {
+ test('should emit error event', () => {
+ expect.hasAssertions()
+ const emitter = new EventEmitter()
+ emitter.on('error', err => {
+ expect(err).toBeInstanceOf(Error)
+ expect(err.message).toBe('test')
+ })
+ emitter.on('test', () => {
+ throw new Error('test')
+ })
+ emitter.emit('test')
+ })
+
+ test('should capture rejections of promises', async () => {
+ expect.hasAssertions()
+ const emitter = new EventEmitter()
+ emitter.on('error', err => {
+ expect(err).toBeInstanceOf(Error)
+ expect(err.message).toBe('test')
+ })
+ emitter.on('test', (async () => {
+ throw new Error('test')
+ }) as any)
+ emitter.emit('test')
+ })
+})
+
+describe('Event: newListener', () => {
+ test('should emit newListener event', () => {
+ class TestEmitter1 extends EventEmitter {}
+ const emitter = new TestEmitter1()
+ const actual: string[] = []
+ emitter.once('newListener', (eventName, listener) => {
+ if (eventName !== 'test') return
+ emitter.on('test', () => { actual.push('newListener') })
+ })
+ expect(emitter.listenerCount('test')).toBe(0)
+ emitter.on('test', () => { actual.push('test') })
+ expect(emitter.listenerCount('test')).toBe(2)
+ emitter.emit('test')
+ expect(actual).toEqual(['newListener', 'test'])
+ })
+})
+
+describe('Event: removeListener', () => {
+ test('should emit removeListener event', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.once('removeListener', (eventName, listener) => {
+ if (eventName !== 'test') return
+ actual.push('removeListener')
+ })
+ emitter.once('test', () => { actual.push('test') })
+ emitter.emit('test')
+ expect(actual).toEqual(['removeListener', 'test'])
+ })
+})
+
+describe('function alias test', () => {
+ test.each([
+ { a: 'addListener', b: 'on' },
+ { a: 'off', b: 'removeListener' },
+ ] as const)('EventEmitter#$a should be alias of EventEmitter#$b', ({ a, b }) => {
+ const emitter = new EventEmitter()
+ expect(emitter[a]).toBe(emitter[b])
+ })
+})
+
+describe('#emit', () => {
+ test('should call all listeners with same arguments', () => {
+ const emitter = new EventEmitter()
+ const actual: any[] = []
+ emitter.on('test', () => {
+ actual.push('first')
+ })
+ emitter.on('test', (arg1, arg2) => {
+ actual.push(`second: ${arg1}, ${arg2}`)
+ })
+ emitter.on('test', (...args) => {
+ actual.push(`third: ${args.join(', ')}`)
+ })
+ emitter.emit('test', 1, 2, 3, 4, 5)
+ expect(actual).toEqual([
+ 'first',
+ 'second: 1, 2',
+ 'third: 1, 2, 3, 4, 5',
+ ])
+ })
+})
+
+describe('#eventNames', () => {
+ test('should return all event names', () => {
+ const emitter = new EventEmitter()
+ emitter.on('foo', () => {})
+ emitter.on('bar', () => {})
+ const sym = Symbol('symbol')
+ emitter.on(sym, () => {})
+
+ const actual = emitter.eventNames()
+ expect(actual).toEqual(['foo', 'bar', sym])
+ })
+})
+
+describe('#getMaxListeners, #setMaxListeners', () => {
+ test('should return the max listeners count', () => {
+ const emitter = new EventEmitter()
+ expect(emitter.getMaxListeners()).toBe(10)
+
+ emitter.setMaxListeners(5)
+ expect(emitter.getMaxListeners()).toBe(5)
+ })
+
+ test('should throw an error when max listeners exceeded', () => {
+ expect.hasAssertions()
+ try {
+ const emitter = new EventEmitter()
+ emitter.setMaxListeners(1)
+ emitter.on('test', () => {})
+ emitter.on('test', () => {})
+ } catch (err) {
+ expect(err).toBeInstanceOf(Error)
+ expect(err.message).toMatch('Max listeners exceeded for event: ')
+ }
+ })
+
+ test('should throw an error when max listeners exceeded', () => {
+ expect.hasAssertions()
+ try {
+ const emitter = new EventEmitter()
+ emitter.setMaxListeners(1)
+ emitter.prependListener('test', () => {})
+ emitter.prependListener('test', () => {})
+ } catch (err) {
+ expect(err).toBeInstanceOf(Error)
+ expect(err.message).toMatch('Max listeners exceeded for event: ')
+ }
+ })
+})
+
+describe('#listenerCount', () => {
+ test('should return the number of listeners for the eventName', () => {
+ const emitter = new EventEmitter()
+ const [listener1, listener2, listener3] = [jest.fn(), jest.fn(), jest.fn()]
+ emitter.on('test', listener1)
+ expect(emitter.listenerCount('test')).toEqual(1)
+ emitter.on('test', listener2)
+ expect(emitter.listenerCount('test')).toEqual(2)
+ emitter.on('test', listener3)
+ expect(emitter.listenerCount('test')).toEqual(3)
+ })
+
+ test('should return 0 when there are no listeners', () => {
+ const emitter = new EventEmitter()
+ const [listener1, listener2, listener3] = [jest.fn(), jest.fn(), jest.fn()]
+ emitter.on('test', listener1)
+ expect(emitter.listenerCount('test', listener1)).toEqual(1)
+ emitter.on('test', listener2)
+ expect(emitter.listenerCount('test', listener1)).toEqual(1)
+
+ emitter.on('test', listener1)
+ expect(emitter.listenerCount('test', listener1)).toEqual(2)
+ emitter.on('test', listener3)
+ expect(emitter.listenerCount('test', listener1)).toEqual(2)
+
+ emitter.on('test', listener1)
+ expect(emitter.listenerCount('test', listener1)).toEqual(3)
+ })
+})
+
+describe('#listeners', () => {
+ test('should return all listeners for the eventName', () => {
+ const emitter = new EventEmitter()
+ const [listener1, listener2, listener3] = [jest.fn(), jest.fn(), jest.fn()]
+ emitter.on('test1', listener1)
+ emitter.on('test2', listener2)
+ emitter.on('test1', listener3)
+ expect(emitter.listeners('test1')).toEqual([listener1, listener3])
+ expect(emitter.listeners('test2')).toEqual([listener2])
+ })
+})
+
+describe('#on', () => {
+ test('should trigger the listener 2 times', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ emitter.on('test', actual)
+ emitter.emit('test')
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(2)
+ })
+
+ test('should return this so that calls can be chained', () => {
+ const emitter = new EventEmitter()
+ const actual = emitter.on('test', () => {})
+ expect(actual).toBe(emitter)
+ })
+
+ test('listeners should be invoked in the order they are added', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.on('test', () => { actual.push('first') })
+ emitter.on('test', () => { actual.push('second') })
+ emitter.emit('test')
+ expect(actual).toEqual(['first', 'second'])
+ })
+
+ test('prependListener() should add the listener to the beginning of the listeners', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.on('test', () => { actual.push('second') })
+ emitter.prependListener('test', () => { actual.push('first') })
+ emitter.emit('test')
+ expect(actual).toEqual(['first', 'second'])
+ })
+})
+
+describe('#once', () => {
+ test('should trigger the listener once', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ emitter.once('test', actual)
+ emitter.emit('test')
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(1)
+ })
+
+ test('should trigger newListener and removeListener events', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.on('newListener', () => { actual.push('newListener') })
+ emitter.on('removeListener', () => { actual.push('removeListener') })
+ emitter.once('test', () => { actual.push('test') })
+ emitter.emit('test')
+ emitter.emit('test')
+ expect(actual).toEqual(['newListener', 'newListener', 'removeListener', 'test'])
+ })
+})
+
+describe('#prependOnceListener', () => {
+ test('should trigger the listener once', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ emitter.prependOnceListener('test', actual)
+ emitter.emit('test')
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(1)
+ })
+
+ test('should trigger newListener and removeListener events', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.on('newListener', () => { actual.push('newListener') })
+ emitter.on('removeListener', () => { actual.push('removeListener') })
+ emitter.prependOnceListener('test', () => { actual.push('test') })
+ emitter.emit('test')
+ emitter.emit('test')
+ expect(actual).toEqual(['newListener', 'newListener', 'removeListener', 'test'])
+ })
+})
+
+describe('#removeAllListeners', () => {
+ test('should remove all listeners for the eventName', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.on('test', () => { actual.push('first') })
+ emitter.on('test', () => { actual.push('second') })
+ emitter.emit('test')
+ emitter.removeAllListeners('test')
+ emitter.emit('test')
+ expect(actual).toEqual(['first', 'second'])
+ })
+
+ test('should remove all listeners', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.on('test1', () => { actual.push('test1') })
+ emitter.on('test2', () => { actual.push('test2') })
+ emitter.removeAllListeners()
+ emitter.emit('test1')
+ emitter.emit('test2')
+ expect(actual).toEqual([])
+ })
+})
+
+describe('#removeListener', () => {
+ test('should remove one listener', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ emitter.on('test', actual)
+ emitter.on('test', actual)
+ emitter.emit('test')
+ emitter.removeListener('test', actual)
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(3)
+ })
+
+ test('cb2 removes cb1 but cb1 should be called', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ const cb1 = (): void => { actual.push('cb1') }
+ const cb2 = (): void => {
+ actual.push('cb2')
+ emitter.removeListener('test', cb1)
+ }
+ emitter.on('test', cb2).on('test', cb1)
+ emitter.emit('test')
+ expect(actual).toEqual(['cb2', 'cb1'])
+ emitter.emit('test')
+ expect(actual).toEqual(['cb2', 'cb1', 'cb2'])
+ })
+
+ test('should remove listener added by #once', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ emitter.once('test', actual)
+ emitter.removeListener('test', actual)
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(0)
+ })
+
+ test('should remove latest listener', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ function pong (): void { actual.push('pong') }
+
+ emitter.on('ping', pong)
+ emitter.once('ping', pong)
+ emitter.removeListener('ping', pong)
+
+ emitter.emit('ping')
+ emitter.emit('ping')
+ expect(actual).toEqual(['pong', 'pong'])
+ })
+
+ test('#removeEventListener', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ emitter.on('test', actual)
+ emitter.on('test', actual)
+ emitter.emit('test')
+ ;(emitter as any).removeEventListener('test', actual)
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(3)
+ })
+
+ test('should no effect if no listeners', () => {
+ const emitter = new EventEmitter()
+ emitter.removeListener('test', () => {})
+ expect(emitter.listenerCount('test')).toBe(0)
+ })
+})
+
+describe('#rawListeners', () => {
+ test('should return a copy of the listeners including wrappers', () => {
+ const emitter = new EventEmitter()
+ const actual: string[] = []
+ emitter.once('test', () => { actual.push('once') })
+
+ const listeners1 = emitter.rawListeners('test')
+ const fnWrapper = listeners1[0]
+
+ ;(fnWrapper as any).listener?.()
+ fnWrapper()
+
+ emitter.on('test', () => { actual.push('on') })
+ const listeners2 = emitter.rawListeners('test')
+ listeners2[0]()
+ emitter.emit('test')
+
+ expect(actual).toEqual(['once', 'once', 'on', 'on'])
+ })
+})
+
+describe('#dispatchEvent', () => {
+ test('should emit the event', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ emitter.on('test', actual)
+ ;(emitter as any).dispatchEvent({ type: 'test' })
+ expect(actual).toHaveBeenCalledTimes(1)
+ })
+})
+
+describe('#addEventListener', () => {
+ test('should add an event listener that can be trigger multiple times', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ ;(emitter as any).addEventListener('test', actual)
+ emitter.emit('test')
+ ;(emitter as any).dispatchEvent({ type: 'test' })
+ expect(actual).toHaveBeenCalledTimes(2)
+ })
+
+ test('should add an event listener that can be trigger once', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ ;(emitter as any).addEventListener('test', actual, { once: true })
+ emitter.emit('test')
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(1)
+ })
+
+ test('should add an event listener and ignore capture boolean', () => {
+ const emitter = new EventEmitter()
+ const actual = jest.fn()
+ ;(emitter as any).addEventListener('test', actual, false)
+ emitter.emit('test')
+ emitter.emit('test')
+ expect(actual).toHaveBeenCalledTimes(2)
+ })
+})
diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts
new file mode 100644
index 0000000..fba3813
--- /dev/null
+++ b/src/EventEmitter.ts
@@ -0,0 +1,154 @@
+import _ from 'lodash'
+const captureRejectionSymbol = Symbol.for('nodejs.rejection')
+
+export class EventEmitter implements NodeJS.EventEmitter, EventTarget {
+ ;[captureRejectionSymbol]?: (err: Error, eventName: EventName, ...args: [] | any[]) => void
+ static readonly defaultMaxListeners = 10
+ #maxListeners = EventEmitter.defaultMaxListeners
+ readonly #eventMap: EventMap = new Map()
+
+ emit (eventName: EventName, ...args: [] | any[]): boolean {
+ const listeners = this.rawListeners(eventName)
+ if (listeners.length === 0) return false
+ for (const listener of listeners) {
+ try {
+ const promise: any | Promise = listener.apply(null, args)
+ if (!_.isFunction(promise?.catch)) continue // not promise
+ promise.catch((err: Error) => {
+ if (err instanceof Error) this.emit('error', err)
+ })
+ } catch (err) {
+ this.emit('error', err)
+ }
+ }
+ return true
+ }
+
+ dispatchEvent (event: DomEvent): boolean {
+ this.emit(event.type, event)
+ return true
+ }
+
+ eventNames (): EventName[] {
+ return [...this.#eventMap.keys()]
+ }
+
+ getMaxListeners (): number {
+ return this.#maxListeners
+ }
+
+ listenerCount (eventName: EventName, listener?: EventListener): number {
+ const listeners = this.rawListeners(eventName)
+ if (listener === undefined) return listeners.length
+ return _.sumBy(listeners, listener2 => isListenerEqual(listener, listener2) ? 1 : 0)
+ }
+
+ listeners (eventName: EventName): EventListener[] {
+ return _.map(this.#eventMap.get(eventName), listener => listener.listener ?? listener)
+ }
+
+ on (eventName: EventName, listener: EventListener): this {
+ const listeners = this.#eventMap.get(eventName) ?? []
+ if (!this.#eventMap.has(eventName)) this.#eventMap.set(eventName, listeners)
+ if (this.#maxListeners > 0 && listeners.length >= this.#maxListeners) throw new Error(`Max listeners exceeded for event: ${String(eventName)}`)
+
+ this.emit('newListener', eventName, listener)
+ listeners.push(listener)
+ return this
+ }
+
+ // @ts-expect-error ts(2416)
+ get addListener (): this['on'] { return this.on }
+
+ addEventListener (eventName: EventName, listener: EventListener, options: boolean | AddEventListenerOptions = {}): this {
+ if (typeof options === 'boolean') options = { capture: options }
+ if (options.once === true) return this.once(eventName, listener)
+ return this.on(eventName, listener)
+ }
+
+ once (eventName: EventName, listener: T): this {
+ return this.on(eventName, onceWrapper(this, eventName, listener))
+ }
+
+ prependListener (eventName: EventName, listener: EventListener): this {
+ const listeners = this.#eventMap.get(eventName) ?? []
+ if (!this.#eventMap.has(eventName)) this.#eventMap.set(eventName, listeners)
+ if (this.#maxListeners > 0 && listeners.length >= this.#maxListeners) throw new Error(`Max listeners exceeded for event: ${String(eventName)}`)
+
+ listeners.unshift(listener)
+ this.emit('newListener', eventName, listener)
+ return this
+ }
+
+ prependOnceListener (eventName: EventName, listener: EventListener): this {
+ return this.prependListener(eventName, onceWrapper(this, eventName, listener))
+ }
+
+ removeAllListeners (eventName?: EventName): this {
+ if (eventName !== undefined) {
+ this.#eventMap.delete(eventName)
+ } else {
+ for (const eventName1 of this.#eventMap.keys()) this.#eventMap.delete(eventName1)
+ }
+ return this
+ }
+
+ removeListener (eventName: EventName, listener: EventListener): this {
+ const listeners = this.#eventMap.get(eventName) ?? []
+ const idx = _.findLastIndex(listeners, listener2 => isListenerEqual(listener, listener2))
+ // console.log(`idx = ${idx}, listeners = ${listeners as unknown as string}`)
+ if (idx >= 0) {
+ listeners.splice(idx, 1)
+ this.emit('removeListener', eventName, listener)
+ }
+ return this
+ }
+
+ removeEventListener (eventName: EventName, listener: EventListener, options: EventListenerOptions = {}): void {
+ this.removeListener(eventName, listener)
+ }
+
+ // @ts-expect-error ts(2416)
+ get off (): this['removeListener'] { return this.removeListener }
+
+ setMaxListeners (num: number): this {
+ this.#maxListeners = num
+ return this
+ }
+
+ rawListeners (eventName: EventName): EventListener[] {
+ return [...(this.#eventMap.get(eventName) ?? [])]
+ }
+}
+
+type EventListener = EventListenerOrig & { listener?: EventListenerOrig }
+type EventListenerOrig = (...args: T) => unknown | Promise
+type EventMap = Map
+type EventName = string | symbol
+
+interface DomEvent {
+ type: string
+}
+
+interface EventListenerOptions {
+ capture?: boolean
+}
+
+interface AddEventListenerOptions extends EventListenerOptions {
+ once?: boolean
+ passive?: boolean
+ signal?: any
+}
+
+function onceWrapper (emitter: EventEmitter, eventName: EventName, listener: EventListener): EventListener {
+ const wrapped = (...args: [] | any[]): void => {
+ emitter.removeListener(eventName, listener)
+ listener.apply(null, args)
+ }
+ wrapped.listener = listener
+ return wrapped
+}
+
+function isListenerEqual (listener1: EventListener, listener2: EventListener): boolean {
+ return (listener1.listener ?? listener1) === (listener2.listener ?? listener2)
+}
diff --git a/src/example/serialport.ts b/src/example/serialport.ts
index fbfee89..e31e03c 100644
--- a/src/example/serialport.ts
+++ b/src/example/serialport.ts
@@ -1,8 +1,10 @@
import { ChameleonUltra } from '../ChameleonUltra'
+import ChameleonDebug from '../plugin/Debug'
import SerialPortAdapter from '../plugin/SerialPortAdapter'
async function main (): Promise {
- const ultra = new ChameleonUltra(true)
+ const ultra = new ChameleonUltra()
+ await ultra.use(new ChameleonDebug())
await ultra.use(new SerialPortAdapter())
console.log(`version: ${await ultra.cmdGetAppVersion()} (${await ultra.cmdGetGitVersion()})`)
diff --git a/src/plugin/Debug.ts b/src/plugin/Debug.ts
new file mode 100644
index 0000000..90e256c
--- /dev/null
+++ b/src/plugin/Debug.ts
@@ -0,0 +1,23 @@
+import createDebugger, { type Debugger } from 'debug'
+import { type ChameleonPlugin, type PluginInstallContext } from '../ChameleonUltra'
+
+export default class ChameleonDebug implements ChameleonPlugin {
+ filter?: ChameleonDebugFilter
+ debugers = new Map()
+ name = 'debug'
+
+ async install (context: PluginInstallContext): Promise {
+ const { ultra } = context
+ ultra.emitter.on('debug', (namespace: string, formatter: any, ...args: [] | any[]) => {
+ if (!(this.filter?.(namespace, formatter, ...args) ?? true)) return
+ const debug = this.debugers.get(namespace) ?? createDebugger(`ultra:${namespace}`)
+ if (!this.debugers.has(namespace)) this.debugers.set(namespace, debug)
+ debug(formatter, ...args)
+ })
+ return this
+ }
+}
+
+;((globalThis as any ?? {}).ChameleonUltraJS ?? {}).ChameleonDebug = ChameleonDebug // eslint-disable-line @typescript-eslint/prefer-optional-chain
+
+type ChameleonDebugFilter = (namespace: string, formatter: any, ...args: [] | any[]) => boolean
diff --git a/src/plugin/SerialPortAdapter.ts b/src/plugin/SerialPortAdapter.ts
index 7863292..06ca76c 100644
--- a/src/plugin/SerialPortAdapter.ts
+++ b/src/plugin/SerialPortAdapter.ts
@@ -1,7 +1,7 @@
import _ from 'lodash'
import { Duplex } from 'stream'
import { SerialPort } from 'serialport'
-import { type ChameleonPlugin, type Logger, type PluginInstallContext } from '../ChameleonUltra'
+import { type ChameleonPlugin, type PluginInstallContext, type ChameleonUltra } from '../ChameleonUltra'
async function findDevicePath (): Promise {
const device = _.find(await SerialPort.list(), { vendorId: '6868', productId: '8686' }) // ChameleonUltra
@@ -11,12 +11,15 @@ async function findDevicePath (): Promise {
export default class SerialPortAdapter implements ChameleonPlugin {
duplex?: SerialPort
- logger: Record = {}
name = 'adapter'
+ ultra?: ChameleonUltra
+
+ #debug (formatter: any, ...args: [] | any[]): void {
+ this.ultra?.emitter.emit('debug', 'serial', formatter, ...args)
+ }
async install (context: AdapterInstallContext, pluginOption: SerialPortOption = {}): Promise {
- const { ultra } = context
- this.logger.serial = ultra.createDebugger('serial')
+ const ultra = this.ultra = context.ultra
if (!_.isNil(ultra.$adapter)) {
await ultra.disconnect(new Error('adapter replaced'))
@@ -39,13 +42,13 @@ export default class SerialPortAdapter implements ChameleonPlugin {
const tmp = new SerialPort({ baudRate, path }, err => { _.isNil(err) ? resolve(tmp) : reject(err) })
})
this.duplex?.once('close', () => { void ultra.disconnect(new Error('SerialPort closed')) })
- this.logger.serial(`port connected, path = ${path}, baudRate = ${baudRate}`)
+ this.#debug(`port connected, path = ${path}, baudRate = ${baudRate}`)
ultra.port = _.merge(Duplex.toWeb(this.duplex), {
isOpen: () => { return this.duplex?.isOpen ?? false },
})
return await next()
} catch (err) {
- this.logger.serial(err)
+ this.#debug(err)
throw err
}
})
diff --git a/src/plugin/WebbleAdapter.ts b/src/plugin/WebbleAdapter.ts
index 36995a5..8012705 100644
--- a/src/plugin/WebbleAdapter.ts
+++ b/src/plugin/WebbleAdapter.ts
@@ -3,7 +3,7 @@ import { ReadableStream, type ReadableStreamDefaultController, type UnderlyingSi
import { sleep } from '../helper'
import { type bluetooth } from 'webbluetooth'
import { type Buffer } from '@taichunmin/buffer'
-import { type ChameleonPlugin, type Logger, type PluginInstallContext } from '../ChameleonUltra'
+import { type ChameleonPlugin, type ChameleonUltra, type PluginInstallContext } from '../ChameleonUltra'
const bluetooth1: typeof bluetooth = (globalThis as any)?.navigator?.bluetooth
const ReadableStream1: typeof ReadableStream = (globalThis as any)?.ReadableStream ?? ReadableStream
@@ -25,18 +25,21 @@ export default class WebbleAdapter implements ChameleonPlugin {
Buffer?: typeof Buffer
device?: BluetoothDevice
isOpen: boolean = false
- logger: Record = {}
name = 'adapter'
recv?: BluetoothRemoteGATTCharacteristic
rxSource?: ChameleonWebbleAdapterRxSource
send?: BluetoothRemoteGATTCharacteristic
serv?: BluetoothRemoteGATTService
txSink?: ChameleonWebbleAdapterTxSink
+ ultra?: ChameleonUltra
+
+ #debug (formatter: any, ...args: [] | any[]): void {
+ this.ultra?.emitter.emit('debug', 'webble', formatter, ...args)
+ }
async install (context: AdapterInstallContext, pluginOption: any): Promise {
const { ultra, Buffer } = context
- this.Buffer = Buffer
- this.logger.webble = ultra.createDebugger('webble')
+ ;[this.ultra, this.Buffer] = [ultra, Buffer]
if (!_.isNil(ultra.$adapter)) await ultra.disconnect(new Error('adapter replaced'))
const adapter: any = {}
@@ -57,19 +60,19 @@ export default class WebbleAdapter implements ChameleonPlugin {
optionalServices: _.uniq(_.map(BLESERIAL_UUID, 'serv')),
})
if (_.isNil(this.device)) throw new Error('no device')
- this.logger.webble(`device selected, name = ${this.device.name ?? 'null'}, id = ${this.device.id}`)
+ this.#debug(`device selected, name = ${this.device.name ?? 'null'}, id = ${this.device.id}`)
this.rxSource = new ChameleonWebbleAdapterRxSource(this)
this.txSink = new ChameleonWebbleAdapterTxSink(this)
for (let i = 0; i < 100; i++) {
- this.logger.webble(`gatt connecting, retry = ${i}`)
- if (!gattIsConnected()) await this.device.gatt?.connect().catch((err: any) => { this.logger.webble(err.message) })
+ this.#debug(`gatt connecting, retry = ${i}`)
+ if (!gattIsConnected()) await this.device.gatt?.connect().catch((err: any) => { this.#debug(err.message) })
// find serv, send, recv, ctrl
// uuid from [bluefy](https://apps.apple.com/app/bluefy-web-ble-browser/id1492822055) is uppercase
const primaryServices = _.map(await this.device.gatt?.getPrimaryServices(), serv => _.toLower(serv.uuid))
- this.logger.webble(`primaryServices = ${JSON.stringify(primaryServices)}`)
+ this.#debug(`primaryServices = ${JSON.stringify(primaryServices)}`)
for (const uuids of BLESERIAL_UUID) {
try {
if (!_.includes(primaryServices, uuids.serv)) continue
@@ -85,7 +88,7 @@ export default class WebbleAdapter implements ChameleonPlugin {
}
if (!_.isNil(this.send) && !_.isNil(this.recv)) {
- this.logger.webble(`gatt connected, serv = ${this.serv?.uuid ?? '?'}, recv = ${this.recv?.uuid ?? '?'}, send = ${this.send?.uuid ?? '?'}'`)
+ this.#debug(`gatt connected, serv = ${this.serv?.uuid ?? '?'}, recv = ${this.recv?.uuid ?? '?'}, send = ${this.send?.uuid ?? '?'}'`)
this.isOpen = true
break
}
@@ -103,7 +106,7 @@ export default class WebbleAdapter implements ChameleonPlugin {
}
return await next()
} catch (err) {
- this.logger.webble(`Failed to connect: ${err.message as string}`)
+ this.#debug(`Failed to connect: ${err.message as string}`)
throw err
}
})
@@ -145,13 +148,17 @@ class ChameleonWebbleAdapterRxSource implements UnderlyingSource {
#controller?: ReadableStreamDefaultController
readonly #adapter: WebbleAdapter
+ #debug (formatter: any, ...args: [] | any[]): void {
+ this.#adapter.ultra?.emitter.emit('debug', 'webble', formatter, ...args)
+ }
+
constructor (adapter: WebbleAdapter) { this.#adapter = adapter }
start (controller: ReadableStreamDefaultController): void { this.#controller = controller }
onNotify (event: any): void {
const buf = this.#adapter.Buffer?.fromView((event?.target?.value as DataView))
- this.#adapter.logger.webble(`onNotify = ${buf?.toString('hex')}`)
+ this.#debug(`onNotify = ${buf?.toString('hex')}`)
this.#controller?.enqueue(buf)
}
}
@@ -166,6 +173,10 @@ class ChameleonWebbleAdapterTxSink implements UnderlyingSink {
this.#Buffer = this.#adapter.Buffer
}
+ #debug (formatter: any, ...args: [] | any[]): void {
+ this.#adapter.ultra?.emitter.emit('debug', 'webble', formatter, ...args)
+ }
+
async write (chunk: Buffer): Promise {
if (_.isNil(this.#adapter.send)) throw new Error('this.#adapter.send can not be null')
@@ -176,7 +187,7 @@ class ChameleonWebbleAdapterTxSink implements UnderlyingSink {
const buf2 = chunk.subarray(i, i + 20)
if (_.isNil(buf1) || buf1.length !== buf2.length) buf1 = new this.#Buffer(buf2.length)
buf1.set(buf2)
- this.#adapter.logger.webble(`bleWrite = ${buf1.toString('hex')}`)
+ this.#debug(`bleWrite = ${buf1.toString('hex')}`)
await this.#adapter.send?.writeValueWithoutResponse(buf1.buffer)
}
}
diff --git a/src/plugin/WebserialAdapter.ts b/src/plugin/WebserialAdapter.ts
index cd79818..e6432f7 100644
--- a/src/plugin/WebserialAdapter.ts
+++ b/src/plugin/WebserialAdapter.ts
@@ -1,22 +1,15 @@
import _ from 'lodash'
-import { sleep } from '../helper'
-import { type ChameleonPlugin, type Logger, type PluginInstallContext } from '../ChameleonUltra'
import { serial, type SerialPort } from 'web-serial-polyfill'
+import { sleep } from '../helper'
+import { type ChameleonPlugin, type ChameleonUltra, type PluginInstallContext } from '../ChameleonUltra'
+import { type EventEmitter } from '../EventEmitter'
-type SerialPort1 = SerialPort & {
- addEventListener: (
- eventName: string,
- listener: (...args: any[]) => void,
- opts?: {
- once: boolean
- }
- ) => any
-}
+type SerialPort1 = SerialPort & EventEmitter
const navigator = (globalThis as any)?.navigator ?? {}
const serial1: typeof serial = navigator.serial ?? ('usb' in navigator ? serial : null)
const WEBSERIAL_FILTERS = [
- { usbVendorId: 0x6868, usbProductId: 0x8686 }, // Chameleon Tiny
+ { usbVendorId: 0x6868, usbProductId: 0x8686 }, // Chameleon Ultra
]
function u16ToHex (num: number): string {
@@ -25,13 +18,16 @@ function u16ToHex (num: number): string {
export default class WebserialAdapter implements ChameleonPlugin {
isOpen: boolean = false
- logger: Record = {}
name = 'adapter'
port?: SerialPort1
+ ultra?: ChameleonUltra
+
+ #debug (formatter: any, ...args: [] | any[]): void {
+ this.ultra?.emitter.emit('debug', 'webserial', formatter, ...args)
+ }
async install (context: AdapterInstallContext, pluginOption: any): Promise {
- const { ultra } = context
- this.logger.webserial = ultra.createDebugger('webserial')
+ const ultra = this.ultra = context.ultra
if (!_.isNil(ultra.$adapter)) await ultra.disconnect(new Error('adapter replaced'))
const adapter: any = {}
@@ -52,14 +48,14 @@ export default class WebserialAdapter implements ChameleonPlugin {
this.isOpen = true
const info = await this.port.getInfo() as { usbVendorId: number, usbProductId: number }
- this.logger.webserial(`port selected, usbVendorId = 0x${u16ToHex(info.usbVendorId)}, usbProductId = 0x${u16ToHex(info.usbProductId)}`)
+ this.#debug(`port selected, usbVendorId = 0x${u16ToHex(info.usbVendorId)}, usbProductId = 0x${u16ToHex(info.usbProductId)}`)
this.port.addEventListener('disconnect', () => { void ultra.disconnect(new Error('Webserial disconnect')) })
ultra.port = _.merge(this.port, {
isOpen: () => this.isOpen,
})
return await next()
} catch (err) {
- this.logger.webserial(err)
+ this.#debug(err)
throw err
}
})
diff --git a/tsup.config.ts b/tsup.config.ts
index e500192..3aa5cb5 100644
--- a/tsup.config.ts
+++ b/tsup.config.ts
@@ -39,6 +39,8 @@ export default defineConfig((options): Options[] => [
minify: !options.watch,
entry: [
'src/Crypto1.ts',
+ 'src/plugin/BufferMockAdapter.ts',
+ 'src/plugin/Debug.ts',
'src/plugin/SerialPortAdapter.ts',
'src/plugin/WebbleAdapter.ts',
'src/plugin/WebserialAdapter.ts',
diff --git a/typedoc.json b/typedoc.json
index f8e0a3e..b5a06e4 100644
--- a/typedoc.json
+++ b/typedoc.json
@@ -1,5 +1,7 @@
{
"cleanOutputDir": false,
+ "customCss": "./typedoc/custom.css",
+ "darkHighlightTheme": "dark-plus",
"exclude": ["**/*.spec.ts", "**/*.test.ts", "**/node_modules/**"],
"includeVersion": true,
"out": "dist",
diff --git a/typedoc/custom.css b/typedoc/custom.css
new file mode 100644
index 0000000..7291df9
--- /dev/null
+++ b/typedoc/custom.css
@@ -0,0 +1,13 @@
+code {
+ :not(pre)>& {
+ background-color: #2c3437;
+ border-radius: 0.4rem;
+ border: .1rem solid rgba(255, 255, 255, .1);
+ color: #e9edf0;
+ font-family: SFMono-Regular, Menlo, Consolas, "Liberation Mono", "Courier New", monospace;
+ font-size: 90%;
+ line-height: 1.5rem;
+ margin: 0;
+ padding: 1px 4px;
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index bf10f95..2920cc3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -309,6 +309,13 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
+ dependencies:
+ "@jridgewell/trace-mapping" "0.3.9"
+
"@esbuild/aix-ppc64@0.19.12":
version "0.19.12"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f"
@@ -824,6 +831,11 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
@@ -839,6 +851,14 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.19"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811"
@@ -1088,10 +1108,10 @@
dependencies:
"@sinonjs/commons" "^3.0.0"
-"@taichunmin/buffer@^0.13.4":
- version "0.13.4"
- resolved "https://registry.yarnpkg.com/@taichunmin/buffer/-/buffer-0.13.4.tgz#8c5519a60c10d6eada4c0a3688f6188454cd0bfd"
- integrity sha512-uwtPpQf7+S0mV6D4oM39NErrVkLkL0+RcyzKLZEkex3umngMhIQH/tin8eW5jGORwJJzcSeSTx58vMXD/jQRqA==
+"@taichunmin/buffer@^0.13.6":
+ version "0.13.6"
+ resolved "https://registry.yarnpkg.com/@taichunmin/buffer/-/buffer-0.13.6.tgz#e75a098498adca2c5b51ca85fc53ac5357633f8b"
+ integrity sha512-3CM5cD1dkWm8l7VFOL0Etz/wQzjHPMTRWQD/QvtSVBBK5uEEvIaQrD/jII+Q+Jy9S6wZZSzadP70vF3Rpuyu0g==
dependencies:
lodash "^4.17.21"
@@ -1100,6 +1120,26 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node-lts/-/node-lts-20.1.3.tgz#8cdf6fa6c593bf5e16e53134ed377627a58a746e"
integrity sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==
+"@tsconfig/node10@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
+ integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==
+
+"@tsconfig/node12@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
+
+"@tsconfig/node14@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
+
+"@tsconfig/node16@^1.0.2":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
+ integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
+
"@types/babel__core@^7.1.14":
version "7.20.2"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.2.tgz#215db4f4a35d710256579784a548907237728756"
@@ -1459,12 +1499,17 @@ acorn-jsx@^5.3.2:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+acorn-walk@^8.1.1:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa"
+ integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==
+
acorn@^7.1.1:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-acorn@^8.11.3, acorn@^8.9.0:
+acorn@^8.11.3, acorn@^8.4.1, acorn@^8.9.0:
version "8.11.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
@@ -1561,6 +1606,11 @@ are-we-there-yet@^3.0.0:
delegates "^1.0.0"
readable-stream "^3.6.0"
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -2116,6 +2166,11 @@ create-jest@^29.7.0:
jest-util "^29.7.0"
prompts "^2.0.1"
+create-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -2238,6 +2293,11 @@ diff-sequences@^29.6.3:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
+diff@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+ integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@@ -4151,7 +4211,7 @@ make-dir@^4.0.0:
dependencies:
semver "^7.5.3"
-make-error@1.x:
+make-error@1.x, make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
@@ -5550,6 +5610,25 @@ ts-jest@^29.1.4:
semver "^7.5.3"
yargs-parser "^21.0.1"
+ts-node@^10.9.2:
+ version "10.9.2"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
+ integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
+ dependencies:
+ "@cspotcode/source-map-support" "^0.8.0"
+ "@tsconfig/node10" "^1.0.7"
+ "@tsconfig/node12" "^1.0.7"
+ "@tsconfig/node14" "^1.0.0"
+ "@tsconfig/node16" "^1.0.2"
+ acorn "^8.4.1"
+ acorn-walk "^8.1.1"
+ arg "^4.1.0"
+ create-require "^1.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ v8-compile-cache-lib "^3.0.1"
+ yn "3.1.1"
+
tsconfig-paths@^3.15.0:
version "3.15.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
@@ -5775,6 +5854,11 @@ utility-types@^3.11.0:
resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.11.0.tgz#607c40edb4f258915e901ea7995607fdf319424c"
integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
v8-to-istanbul@^9.0.1:
version "9.1.0"
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265"
@@ -5964,6 +6048,11 @@ yargs@^17.3.1, yargs@^17.6.0, yargs@^17.7.2:
y18n "^5.0.5"
yargs-parser "^21.1.1"
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
+
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"