feat: send data to and from serial port
This commit is contained in:
parent
ae68c4dc6e
commit
7594474189
3 changed files with 184 additions and 79 deletions
|
|
@ -33,6 +33,16 @@ new Vue({
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
serial: {
|
||||||
|
selectedPort: null,
|
||||||
|
writableStreamClosed: null,
|
||||||
|
writer: null,
|
||||||
|
readableStreamClosed: null,
|
||||||
|
reader: null,
|
||||||
|
showAdvancedConfig: false,
|
||||||
|
config: {}
|
||||||
|
},
|
||||||
|
|
||||||
formDialog: {
|
formDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
data: {}
|
data: {}
|
||||||
|
|
@ -500,8 +510,7 @@ new Vue({
|
||||||
LNbits.utils.notifyApiError(err)
|
LNbits.utils.notifyApiError(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sharePsbtOnSerialPort: async function () {
|
checkSerialPortSupported: function () {
|
||||||
console.log('### sharePsbtOnSerialPort', navigator.serial, navigator)
|
|
||||||
if (!navigator.serial) {
|
if (!navigator.serial) {
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
|
|
@ -510,8 +519,14 @@ new Vue({
|
||||||
'Make sure your browser supports Serial Port and that you are using HTTPS.',
|
'Make sure your browser supports Serial Port and that you are using HTTPS.',
|
||||||
timeout: 10000
|
timeout: 10000
|
||||||
})
|
})
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
openSerialPort: async function () {
|
||||||
|
if (!this.checkSerialPortSupported()) return
|
||||||
|
console.log('### openSerialPort', this.serial.selectedPort)
|
||||||
|
try {
|
||||||
navigator.serial.addEventListener('connect', event => {
|
navigator.serial.addEventListener('connect', event => {
|
||||||
console.log('### navigator.serial event: connected!', event)
|
console.log('### navigator.serial event: connected!', event)
|
||||||
})
|
})
|
||||||
|
|
@ -519,37 +534,104 @@ new Vue({
|
||||||
navigator.serial.addEventListener('disconnect', event => {
|
navigator.serial.addEventListener('disconnect', event => {
|
||||||
console.log('### navigator.serial event: disconnected!', event)
|
console.log('### navigator.serial event: disconnected!', event)
|
||||||
})
|
})
|
||||||
try {
|
this.serial.selectedPort = await navigator.serial.requestPort()
|
||||||
// const ports = await navigator.serial.getPorts();
|
|
||||||
const port = await navigator.serial.requestPort()
|
|
||||||
console.log('### port', port)
|
|
||||||
|
|
||||||
// Wait for the serial port to open.
|
// Wait for the serial port to open.
|
||||||
await port.open({baudRate: 9600})
|
await this.serial.selectedPort.open({baudRate: 9600})
|
||||||
|
this.startSerialPortReading()
|
||||||
|
|
||||||
const writer = port.writable.getWriter()
|
const textEncoder = new TextEncoderStream()
|
||||||
|
this.serial.writableStreamClosed = textEncoder.readable.pipeTo(
|
||||||
const psbtByteArray = Uint8Array.from(
|
this.serial.selectedPort.writable
|
||||||
atob(this.payment.psbtBase64),
|
|
||||||
c => c.charCodeAt(0)
|
|
||||||
)
|
)
|
||||||
await writer.write(psbtByteArray)
|
|
||||||
|
|
||||||
// Allow the serial port to be closed later.
|
this.serial.writer = textEncoder.writable.getWriter()
|
||||||
writer.releaseLock()
|
|
||||||
|
|
||||||
await port.close()
|
|
||||||
console.log('### sharePsbtOnSerialPort done')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('### error', error)
|
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: 'Serial port communication failed!',
|
message: 'Cannot open serial port!',
|
||||||
caption: `${error}`,
|
caption: `${error}`,
|
||||||
timeout: 10000
|
timeout: 10000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
closeSerialPort: async function () {
|
||||||
|
try {
|
||||||
|
console.log('### closeSerialPort', this.serial.selectedPort)
|
||||||
|
if (this.serial.writer) this.serial.writer.close()
|
||||||
|
if (this.serial.writableStreamClosed)
|
||||||
|
await this.serial.writableStreamClosed
|
||||||
|
if (this.serial.reader) this.reader.writer.close()
|
||||||
|
if (this.serial.readableStreamClosed)
|
||||||
|
await this.serial.readableStreamClosed
|
||||||
|
if (this.serial.selectedPort) await this.serial.selectedPort.close()
|
||||||
|
this.serial.selectedPort = null
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Cannot close serial port!',
|
||||||
|
caption: `${error}`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sendPsbtToSerialPort: async function () {
|
||||||
|
try {
|
||||||
|
await this.serial.writer.write(this.payment.psbtBase64 + '\n')
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: 'Data sent to serial port!',
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Failed to send data to serial port!',
|
||||||
|
caption: `${error}`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
startSerialPortReading: async function () {
|
||||||
|
const port = this.serial.selectedPort
|
||||||
|
|
||||||
|
while (port && port.readable) {
|
||||||
|
const textDecoder = new TextDecoderStream()
|
||||||
|
this.serial.readableStreamClosed = port.readable.pipeTo(
|
||||||
|
textDecoder.writable
|
||||||
|
)
|
||||||
|
this.serial.reader = textDecoder.readable.getReader()
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
console.log('### reader.read()')
|
||||||
|
const {value, done} = await this.serial.reader.read()
|
||||||
|
if (value) {
|
||||||
|
console.log(value)
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Received data from serial port (not psbt)',
|
||||||
|
caption: value.slice(0, 80) + '...',
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (done) {
|
||||||
|
this.serial.reader.close()
|
||||||
|
this.serial.readereadableStreamClosed()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Serial port communication error!',
|
||||||
|
caption: `${error}`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('### startSerialPortReading DONE')
|
||||||
|
},
|
||||||
sharePsbtWithAnimatedQRCode: async function () {
|
sharePsbtWithAnimatedQRCode: async function () {
|
||||||
console.log('### sharePsbtWithAnimatedQRCode')
|
console.log('### sharePsbtWithAnimatedQRCode')
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,7 @@ const tableData = {
|
||||||
fee: 0,
|
fee: 0,
|
||||||
txSize: 0,
|
txSize: 0,
|
||||||
psbtBase64: '',
|
psbtBase64: '',
|
||||||
|
psbtBase64Signed: '',
|
||||||
utxoSelectionModes: [
|
utxoSelectionModes: [
|
||||||
'Manual',
|
'Manual',
|
||||||
'Random',
|
'Random',
|
||||||
|
|
@ -268,6 +269,18 @@ const tableData = {
|
||||||
'Larger Inputs First'
|
'Larger Inputs First'
|
||||||
],
|
],
|
||||||
utxoSelectionMode: 'Manual',
|
utxoSelectionMode: 'Manual',
|
||||||
|
signModes: [
|
||||||
|
{
|
||||||
|
label: 'Serial Port',
|
||||||
|
value: 'serial-port'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Animated QR',
|
||||||
|
value: 'animated-qr',
|
||||||
|
disable: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
signMode: '',
|
||||||
show: false,
|
show: false,
|
||||||
showAdvanced: false
|
showAdvanced: false
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1009,72 +1009,82 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
<div class="col-4 q-pr-lg">
|
<div class="col-3 q-pr-lg">
|
||||||
<q-btn unelevated color="secondary" type="submit"
|
<q-btn unelevated color="secondary" type="submit"
|
||||||
>Create PSBT</q-btn
|
>Create PSBT</q-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-9">
|
||||||
<q-btn-dropdown
|
<q-input
|
||||||
v-if="payment.psbtBase64"
|
v-if="payment.psbtBase64"
|
||||||
split
|
v-model="payment.psbtBase64"
|
||||||
class="float-right"
|
filled
|
||||||
color="secondary"
|
readonly
|
||||||
label="Share PSBT"
|
|
||||||
@click="sharePsbtOnSerialPort"
|
|
||||||
>
|
|
||||||
<q-list>
|
|
||||||
<q-item
|
|
||||||
@click="sharePsbtOnSerialPort"
|
|
||||||
clickable
|
|
||||||
v-close-popup
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-avatar
|
|
||||||
icon="usb"
|
|
||||||
color="primary"
|
|
||||||
text-color="white"
|
|
||||||
/>
|
/>
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Serial Port</q-item-label>
|
|
||||||
<q-item-label caption
|
|
||||||
>Send the PSBT using an USB port
|
|
||||||
</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
|
|
||||||
<q-item
|
|
||||||
@click="sharePsbtWithAnimatedQRCode"
|
|
||||||
disabled
|
|
||||||
v-close-popup
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-avatar
|
|
||||||
icon="qr_code"
|
|
||||||
color="secondary"
|
|
||||||
text-color="white"
|
|
||||||
/>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Animated QR Code</q-item-label>
|
|
||||||
<q-item-label caption>Comming Soon</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-btn-dropdown>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="payment.psbtBase64"
|
v-if="payment.psbtBase64"
|
||||||
class="row items-center no-wrap q-mb-md"
|
class="row items-center no-wrap q-mb-md"
|
||||||
>
|
>
|
||||||
<div class="col-12">
|
<div class="col-3">Sign With</div>
|
||||||
|
<div class="col-9">
|
||||||
|
<q-option-group
|
||||||
|
v-model="payment.signMode"
|
||||||
|
:options="payment.signModes"
|
||||||
|
color="primary"
|
||||||
|
label="Sign Mode"
|
||||||
|
inline
|
||||||
|
></q-option-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<div
|
||||||
|
v-if="payment.psbtBase64 && payment.signMode === 'serial-port'"
|
||||||
|
class="row items-center no-wrap q-mb-md q-mt-lg"
|
||||||
|
>
|
||||||
|
<div class="col-3"></div>
|
||||||
|
<div class="col-3">
|
||||||
|
<q-btn
|
||||||
|
v-if="!serial.selectedPort"
|
||||||
|
@click="openSerialPort()"
|
||||||
|
unelevated
|
||||||
|
color="secondary"
|
||||||
|
>Connect</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
v-if="serial.selectedPort"
|
||||||
|
@click="closeSerialPort()"
|
||||||
|
outline
|
||||||
|
color="gray"
|
||||||
|
>Disconnect</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<q-btn
|
||||||
|
v-if="serial.selectedPort"
|
||||||
|
@click="sendPsbtToSerialPort()"
|
||||||
|
unelevated
|
||||||
|
color="secondary float-right"
|
||||||
|
>Send PSBT</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<q-toggle
|
||||||
|
label="Advanced Config"
|
||||||
|
color="secodary float-right"
|
||||||
|
v-model="serial.showAdvancedConfig"
|
||||||
|
></q-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col-4 q-pr-lg"></div>
|
||||||
|
<div class="col-8">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="payment.psbtBase64"
|
v-if="payment.psbtBase64Signed"
|
||||||
|
v-model="payment.psbtBase64Signed"
|
||||||
filled
|
filled
|
||||||
readonly
|
readonly
|
||||||
type="textarea"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue