diff --git a/pug/src/dfu.pug b/pug/src/dfu.pug
index 8122d40..478f1b2 100644
--- a/pug/src/dfu.pug
+++ b/pug/src/dfu.pug
@@ -33,11 +33,23 @@ block content
option(value="ble") BLE (PC & Android)
option(value="usb") USB Serial (PC only)
.input-group-append: button.btn.btn-outline-secondary(@click="btnAdapterTips") #[i.fa.fa-fw.fa-question]
- .form-group.letter-spacing-n1px.mb-3
- label Tag Name:
- select.form-control.form-control-sm(v-model="ss.tagName")
- option(v-for="[k, v] of tagNames", :value="k") {{ v }}
- button.btn.btn-block.btn-outline-primary.letter-spacing-n1px.mb-2(@click="btnUploadFirmware") #[i.fa.mr-1.fa-download] Upload Firmware
+ .card.shadow-sm.mb-2
+ h6.card-header #[i.fa.fa-fw.fa-github] From GitHub
+ .card-body.px-3.pt-3.pb-2.letter-spacing-n1px
+ .form-group.letter-spacing-n1px.mb-3
+ label Tag Name:
+ select.form-control.form-control-sm(v-model="ss.tagName")
+ option(v-for="[k, v] of tagNames", :value="k") {{ v }}
+ button.btn.btn-block.btn-outline-primary.letter-spacing-n1px.mb-2(@click="btnUploadRelease") #[i.fa.mr-1.fa-puzzle-piece] Upload Firmware
+ .card.shadow-sm.mb-2
+ h6.card-header #[i.fa.fa-fw.fa-file-archive-o] From Zip File
+ .card-body.px-3.pt-3.pb-2.letter-spacing-n1px
+ .form-group.letter-spacing-n1px.mb-3
+ label Choose firmware:
+ .custom-file.form-control-sm.letter-spacing-n1px
+ input.custom-file-input#imageSelected(type="file", v-model="imageSelected", ref="imageSelected", accept=".zip")
+ label.custom-file-label(for="imageSelected") {{ getFilename(imageSelected) || 'Choose firmware...' }}
+ button.btn.btn-block.btn-outline-primary.letter-spacing-n1px.mb-2(@click="btnUploadSelected") #[i.fa.mr-1.fa-puzzle-piece] Upload Firmware
block script
script.
@@ -58,6 +70,7 @@ block script
ss: {
tagName: '',
},
+ imageSelected: '',
manifest: {},
},
async mounted () {
@@ -109,7 +122,7 @@ block script
this.ss.tagName = _.first(this.tagNames)[0]
Swal.close()
},
- async btnUploadFirmware () {
+ async btnUploadRelease () {
const { ultra } = this
const numFormater = new Intl.NumberFormat('en', { minimumFractionDigits: 1, maximumFractionDigits: 1 })
const showProgress = ({ func, offset, size, type }) => {
@@ -129,14 +142,22 @@ block script
return await dfuZip.getAppImage()
}))
this.showLoading({ text: 'Connect device...' })
- await ultra.connect()
+ if (!ultra.isConnected()) await ultra.connect()
if (!ultra.isDfu()) {
- const gitVersion = await ultra.cmdGetGitVersion()
- if (!_.isNil(release.gitVersion) && release.gitVersion === gitVersion) {
- const msg1 = `gitVersion(${gitVersion}) is the same, do you want to upload again?`
- if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
+ try {
+ const gitVersion = await ultra.cmdGetGitVersion()
+ if (!_.isNil(release.gitVersion) && release.gitVersion === gitVersion) {
+ const msg1 = `gitVersion(${gitVersion}) is the same, do you want to upload again?`
+ if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
+ }
+ } catch (err) {
+ // 有可能是因為不支援 cmdGetGitVersion
+ ultra.emitter.emit('error', _.set(new Error(err.message), 'originalError', err))
}
await ultra.cmdDfuEnter()
+ await Swal.fire({ icon: 'info', text: 'Please reconnect your device!' })
+ if (!ultra.isConnected()) await ultra.connect()
+ if (!ultra.isDfu()) throw new Error('Device is not in DFU mode')
}
this.showLoading({ text: 'Uploading Firmware...' })
ultra.emitter.on('progress', showProgress)
@@ -158,6 +179,50 @@ block script
}
ultra.emitter.removeListener('progress', showProgress)
},
+ async btnUploadSelected () {
+ const { ultra } = this
+ const numFormater = new Intl.NumberFormat('en', { minimumFractionDigits: 1, maximumFractionDigits: 1 })
+ const showProgress = ({ func, offset, size, type }) => {
+ if (func !== 'dfuUploadObject' || type !== 2) return
+ const width = _.round(size > 0 ? offset / size * 100 : 0, 1)
+ const html = `
Uploading:${numFormater.format(width)} %
`
+ this.showLoading({ html })
+ }
+ try {
+ this.showLoading({ text: 'Unzip firmware...' })
+ const dfuFile = this.$refs.imageSelected.files[0]
+ if (_.isNil(dfuFile)) throw new Error('Please browse a firmware file.')
+ const dfuZip = new DfuZip(new Buffer(await dfuFile.arrayBuffer()))
+ const image = await dfuZip.getAppImage()
+ const imageGitVersion = await dfuZip.getGitVersion()
+ this.showLoading({ text: 'Connect device...' })
+ if (!ultra.isConnected()) await ultra.connect()
+ if (!ultra.isDfu()) {
+ try {
+ const gitVersion = await ultra.cmdGetGitVersion()
+ if (!_.isNil(imageGitVersion) && imageGitVersion === gitVersion) {
+ const msg1 = `gitVersion(${gitVersion}) is the same, do you want to upload again?`
+ if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
+ }
+ } catch (err) {
+ // 有可能是因為不支援 cmdGetGitVersion
+ ultra.emitter.emit('error', _.set(new Error(err.message), 'originalError', err))
+ }
+ await ultra.cmdDfuEnter()
+ await Swal.fire({ icon: 'info', text: 'Please reconnect your device!' })
+ if (!ultra.isConnected()) await ultra.connect()
+ if (!ultra.isDfu()) throw new Error('Device is not in DFU mode')
+ }
+ this.showLoading({ text: 'Uploading Firmware...' })
+ ultra.emitter.on('progress', showProgress)
+ await ultra.dfuUploadImage(image)
+ await Swal.fire({ icon: 'success', title: 'Upload Success' })
+ } catch (err) {
+ ultra.emitter.emit('error', err)
+ await Swal.fire({ icon: 'error', title: 'Upload Failed', text: err.message })
+ }
+ ultra.emitter.removeListener('progress', showProgress)
+ },
async sleep (t) {
await new Promise(resolve => setTimeout(resolve, t))
},
@@ -179,6 +244,9 @@ block script
Swal.fire(args).then(res => { isConfirmed = res.isConfirmed })
})
},
+ getFilename (str) {
+ return str.replaceAll(/.*[/\\]/g, '')
+ },
showLoading (opts = {}) {
opts = {
allowOutsideClick: false,
diff --git a/pug/src/mfkey32.pug b/pug/src/mfkey32.pug
index 830ea3e..17ebd56 100644
--- a/pug/src/mfkey32.pug
+++ b/pug/src/mfkey32.pug
@@ -31,7 +31,7 @@ block content
option(value="usb") USB Serial (PC only)
.input-group-append: button.btn.btn-outline-secondary(@click="btnAdapterTips") #[i.fa.fa-fw.fa-question]
.card.mb-3
- .card-header.letter-spacing-n1px #[i.fa.fa-fw.fa-id-card] Scan UID / Emulate Tag
+ h6.card-header #[i.fa.fa-fw.fa-id-card] Scan UID / Emulate Tag
.card-body
.input-group.input-group-sm.mb-2
.input-group-prepend: span.input-group-text.justify-content-center Slot
@@ -58,7 +58,7 @@ block content
.col.px-1: button.btn.btn-block.btn-success.letter-spacing-n1px(@click="btnScanTag") #[i.fa.fa-fw.fa-sign-out] Scan UID
.col.px-1: button.btn.btn-block.btn-primary.letter-spacing-n1px(@click="btnEmulateTag") #[i.fa.fa-fw.fa-sign-in] Emulate
.card.mb-3
- .card-header.letter-spacing-n1px #[i.fa.fa-fw.fa-lock] Key recover / Check key
+ h6.card-header #[i.fa.fa-fw.fa-lock] Key recover / Check key
.card-body
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center Block
diff --git a/src/ChameleonUltra.ts b/src/ChameleonUltra.ts
index bce52d1..0b66dc7 100644
--- a/src/ChameleonUltra.ts
+++ b/src/ChameleonUltra.ts
@@ -340,7 +340,7 @@ export class ChameleonUltra {
/**
* Read a response from device.
- * @param timeout - The timeout in milliseconds.
+ * @param args.timeout - The timeout in milliseconds.
* @internal
* @group Internal
*/
@@ -1759,8 +1759,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.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`.
+ * @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`.
* @returns The response from tag.
* @group Reader Related
*/
diff --git a/src/plugin/Debug.ts b/src/plugin/Debug.ts
index a034ea7..f460295 100644
--- a/src/plugin/Debug.ts
+++ b/src/plugin/Debug.ts
@@ -1,14 +1,18 @@
import _ from 'lodash'
+import { type Buffer } from '@taichunmin/buffer'
import { type ChameleonPlugin, type PluginInstallContext as ChameleonCtx } from '../ChameleonUltra'
import createDebugger, { type Debugger } from 'debug'
+let Buffer1: typeof Buffer
+
export default class Debug implements ChameleonPlugin {
- filter?: DebugFilter
debugers = new Map()
+ filter?: DebugFilter
name = 'debug'
async install (context: ChameleonCtx): Promise {
const { ultra } = context
+ if (_.isNil(Buffer1)) Buffer1 = context.Buffer
ultra.emitter.on('error', (err: Error) => {
const errJson = errToJson(err)
ultra.emitter.emit('debug', 'error', jsonStringify(errJson))
@@ -68,6 +72,7 @@ export function stringifyClone (obj: any): any {
if (preventCircular.has(val1)) return '[Circular]'
preventCircular.add(val1)
}
+ if (Buffer1?.isBuffer(val1)) return { type: 'Buffer', hex: val1.toString('hex') }
if (typeof val1 === 'bigint') return val1.toString()
if (val1 instanceof Error) return errToJson(val1)
if (val1 instanceof Map) return _.fromPairs([...val1.entries()])
@@ -90,3 +95,8 @@ export function stringifyReplacer (this: any, key: any, val: any): any {
export function jsonStringify (obj: object, space?: number): string {
return JSON.stringify(stringifyClone(obj), stringifyReplacer, space)
}
+
+export function arrayBufferViewToHex (view: ArrayBufferView): string {
+ const u8 = new Uint8Array(view.buffer, view.byteOffset, view.byteLength)
+ return _.map(u8, b => `0${b.toString(16)}`.slice(-2)).join('')
+}
diff --git a/src/plugin/DfuZip.ts b/src/plugin/DfuZip.ts
index f91f8e9..1ff6136 100644
--- a/src/plugin/DfuZip.ts
+++ b/src/plugin/DfuZip.ts
@@ -43,6 +43,13 @@ export default class DfuZip {
async getAppImage (): Promise {
return await this.getImage(['application'])
}
+
+ async getGitVersion (): Promise {
+ const image = await this.getAppImage()
+ if (_.isNil(image)) return null
+ // eslint-disable-next-line no-control-regex
+ return image.body.toString('utf8').match(/\x00(v\d+(?:\.\d+)*[\w-]*)\x00/)?.[1] ?? null
+ }
}
;((globalThis as any ?? {}).ChameleonUltraJS ?? {}).DfuZip = DfuZip // eslint-disable-line @typescript-eslint/prefer-optional-chain
diff --git a/tsup.config.ts b/tsup.config.ts
index b9a8bd6..eb6a54d 100644
--- a/tsup.config.ts
+++ b/tsup.config.ts
@@ -39,7 +39,6 @@ export default defineConfig((options): Options[] => [
minify: !options.watch,
entry: [
'src/Crypto1.ts',
- 'src/plugin/BufferMockAdapter.ts',
'src/plugin/Debug.ts',
'src/plugin/DfuZip.ts',
'src/plugin/SerialPortAdapter.ts',