feat: remember paired devices

This commit is contained in:
Vlad Stan 2022-08-19 14:59:59 +03:00
parent 713909292e
commit 83f998fe37
5 changed files with 129 additions and 80 deletions

View File

@ -1,4 +1,15 @@
<div>
<div class="row q-mt-md">
<div class="col-12">
<q-input
filled
dense
v-model.trim="config.name"
label="Name (optional)"
></q-input>
</div>
</div>
<q-separator class="q-mt-sm"></q-separator>
<div class="row q-mt-md">
<div class="col-12">
<q-input
@ -64,4 +75,28 @@
></q-input>
</div>
</div>
<q-separator class="q-mt-sm"></q-separator>
<div class="row q-mt-md">
<div class="col-12">
<q-input
filled
dense
v-model.trim="config.buttonOnePin"
type="number"
label="Pin Number (Button 1)"
></q-input>
</div>
</div>
<div class="row q-mt-md">
<div class="col-12">
<q-input
filled
dense
v-model.trim="config.buttonTwoPin"
type="number"
label="Pin Number (Button 2)"
></q-input>
</div>
</div>
</div>

View File

@ -2,23 +2,15 @@ async function serialPortConfig(path) {
const t = await loadTemplateAsync(path)
Vue.component('serial-port-config', {
name: 'serial-port-config',
props: ['config'],
template: t,
data() {
return {
config: {
baudRate: 9600,
bufferSize: 255,
dataBits: 8,
flowControl: 'none',
parity: 'none',
stopBits: 1
}
}
},
methods: {
getConfig: function () {
return this.config
}
}
},
})
}

View File

@ -46,6 +46,27 @@
>
</q-item-section>
</q-item>
<q-item
v-for="device in pairedDevices"
:key="device.id"
v-if="!selectedPort"
clickable
v-close-popup
>
<q-item-section>
<q-item-label @click="openSerialPortConfig(device.id)"
>Paired Device ({{device.config.name || 'no-name'}})
</q-item-label>
<q-item-label caption @click="openSerialPortConfig(device.id)"
>{{device.id}}
</q-item-label>
<q-item-label caption @click="removePairedDevice(device.id)">
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
>Forget</q-btn
>
</q-item-label>
</q-item-section>
</q-item>
<q-item
v-if="selectedPort"
clickable
@ -123,33 +144,10 @@
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="hwwConfigAndConnect" class="q-gutter-md">
<span>Enter Config</span>
<q-input
filled
dense
v-model.trim="hww.deviceConfig.name"
label="Name (optional)"
></q-input>
<q-separator></q-separator>
<serial-port-config
ref="serialPortConfig"
:config="hww.config"
:config="config"
></serial-port-config>
<q-separator></q-separator>
<q-input
filled
dense
v-model.trim="hww.deviceConfig.buttonOnePin"
type="number"
label="Pin Number (Button 1)"
></q-input>
<q-input
filled
dense
v-model.trim="hww.deviceConfig.buttonTwoPin"
type="number"
label="Pin Number (Button 2)"
></q-input>
<div class="row q-mt-lg">
<q-btn unelevated color="primary" type="submit">Connect</q-btn>

View File

@ -36,12 +36,8 @@ async function serialSigner(path) {
xpubResolve: null,
seedWordPosition: 1,
showSeedDialog: false,
config: null,
deviceConfig: {
name: null,
buttonOnePin: 0,
buttonTwoPin: 35
},
// config: null,
confirm: {
outputIndex: 0,
showFee: false
@ -53,12 +49,30 @@ async function serialSigner(path) {
}
},
computed: {
pairedDevices: {
get: function () {
return (
JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) ||
[]
)
},
set: function (devices) {
window.localStorage.setItem(
'lnbits-paired-devices',
JSON.stringify(devices)
)
}
}
},
methods: {
satBtc(val, showUnit = true) {
return satOrBtc(val, showUnit, this.satsDenominated)
},
openSerialPortDialog: async function () {
await this.openSerialPort()
this.config = {...HWW_DEFAULT_CONFIG}
await this.openSerialPort(this.config)
},
openSerialPort: async function (config = {baudRate: 9600}) {
if (!this.checkSerialPortSupported()) return false
@ -74,11 +88,10 @@ async function serialSigner(path) {
try {
this.selectedPort = await navigator.serial.requestPort()
this.selectedPort.addEventListener('connect', event => {
console.log('### this.selectedPort event: connected!', event)
// do nothing
})
this.selectedPort.addEventListener('disconnect', () => {
console.log('### this.selectedPort event: disconnected!', event)
this.selectedPort = null
this.hww.authenticated = false
this.$q.notify({
@ -113,7 +126,13 @@ async function serialSigner(path) {
return false
}
},
openSerialPortConfig: async function () {
openSerialPortConfig: async function (deviceId) {
const device = this.getPairedDevice(deviceId)
if (device) {
this.config = device.config
} else {
this.config = {...HWW_DEFAULT_CONFIG}
}
this.hww.showConfigDialog = true
},
closeSerialPort: async function () {
@ -289,7 +308,6 @@ async function serialSigner(path) {
}
},
handlePingResponse: function (res = '') {
console.log('### handlePingResponse', res)
const [status, deviceId] = res.split(' ')
this.deviceId = deviceId
@ -373,8 +391,10 @@ async function serialSigner(path) {
},
hwwConfigAndConnect: async function () {
this.hww.showConfigDialog = false
const config = this.$refs.serialPortConfig.getConfig()
await this.openSerialPort(config)
if (this.config.deviceId) {
this.updatePairedDeviceConfig(this.config.deviceId, this.config)
}
await this.openSerialPort(this.config)
return true
},
hwwLogin: async function () {
@ -525,7 +545,6 @@ async function serialSigner(path) {
},
hwwCheckPairing: async function () {
const iv = window.crypto.getRandomValues(new Uint8Array(16))
console.log('### this.sharedSecret', this.sharedSecret)
const encrypted = await this.encryptMessage(
this.sharedSecret,
iv,
@ -582,8 +601,8 @@ async function serialSigner(path) {
await this.sendCommandClearText(COMMAND_PAIR, [
publicKeyHex,
this.hww.deviceConfig.buttonOnePin,
this.hww.deviceConfig.buttonTwoPin
this.config.buttonOnePin,
this.config.buttonTwoPin
])
this.$q.notify({
type: 'positive',
@ -600,7 +619,6 @@ async function serialSigner(path) {
}
},
handlePairResponse: async function (res = '') {
console.log('### handlePairResponse', res)
const [statusCode, data] = res.trim().split(' ')
let pubKeyHex, errorMessage, captionMessage
switch (statusCode) {
@ -645,15 +663,14 @@ async function serialSigner(path) {
.bytesToHex(sharedSecredHash)
.substring(0, 5)
.toUpperCase()
console.log('### fingerprint', fingerprint)
//
LNbits.utils
.confirmDialog('Confirm code from display: ' + fingerprint)
.onOk(() => {
this.addPairedDevice(
this.deviceId,
nobleSecp256k1.utils.bytesToHex(this.sharedSecret)
nobleSecp256k1.utils.bytesToHex(this.sharedSecret),
this.config
)
this.$q.notify({
@ -773,7 +790,6 @@ async function serialSigner(path) {
},
handleShowSeedResponse: function (res = '') {
const args = res.trim().split(' ')
console.log('### handleShowSeedResponse: ', res)
},
hwwRestore: async function () {
try {
@ -817,7 +833,6 @@ async function serialSigner(path) {
},
sendCommandClearText: async function (command, attrs = []) {
const message = [command].concat(attrs).join(' ')
console.log('### encryptedIvHex', message)
await this.writer.write(message + '\n')
},
extractCommand: async function (value) {
@ -863,7 +878,6 @@ async function serialSigner(path) {
const command = data
.substring(len.length + 1, +len + len.length + 1)
.trim()
console.log('### decryptData ', data, command)
return command
} catch (error) {
return '/error Failed to decrypt message from device!'
@ -883,37 +897,35 @@ async function serialSigner(path) {
const decryptedBytes = aesCbc.decrypt(encryptedBytes)
return decryptedBytes
},
getPairedDevices: function () {
console.log(
'### getPairedDevices',
window.localStorage.getItem('lnbits-paired-devices')
)
return (
JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) || []
)
},
getPairedDevice: function (deviceId) {
const devices = this.getPairedDevices()
return devices.find(d => d.id === deviceId)
return this.pairedDevices.find(d => d.id === deviceId)
},
removePairedDevice: function (deviceId) {
const devices = this.getPairedDevices()
const deviceIndex = devices.indexOf(d => d.id === deviceId)
const devices = this.pairedDevices
const deviceIndex = devices.findIndex(d => d.id === deviceId)
if (deviceIndex !== -1) {
devices.splice(deviceIndex, 1)
}
this.pairedDevices = devices
},
addPairedDevice: function (deviceId, sharedSecretHex) {
const devices = this.getPairedDevices()
devices.push({
addPairedDevice: function (deviceId, sharedSecretHex, config) {
const devices = this.pairedDevices
config.deviceId = deviceId
devices.unshift({
id: deviceId,
sharedSecretHex: sharedSecretHex,
pairingDate: new Date().toISOString()
pairingDate: new Date().toISOString(),
config
})
window.localStorage.setItem(
'lnbits-paired-devices',
JSON.stringify(devices)
)
this.pairedDevices = devices
},
updatePairedDeviceConfig(deviceId, config) {
const device = this.getPairedDevice(deviceId)
if (device) {
this.removePairedDevice(deviceId)
this.addPairedDevice(deviceId, device.sharedSecretHex, config)
}
}
},
created: async function () {}

View File

@ -19,6 +19,18 @@ const COMMAND_CHECK_PAIRING = '/check-pairing'
const DEFAULT_RECEIVE_GAP_LIMIT = 20
const PAIRING_CONTROL_TEXT = 'lnbits'
const HWW_DEFAULT_CONFIG = Object.freeze({
name: '',
buttonOnePin: 0,
buttonTwoPin: 35,
baudRate: 9600,
bufferSize: 255,
dataBits: 8,
flowControl: 'none',
parity: 'none',
stopBits: 1
})
const blockTimeToDate = blockTime =>
blockTime ? moment(blockTime * 1000).format('LLL') : ''