@onekeyfe/hd-transport-electron 1.0.34-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +123 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +37 -0
- package/src/index.ts +195 -0
- package/src/types.ts +68 -0
- package/tsconfig.json +11 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { WebContents } from 'electron';
|
|
2
|
+
|
|
3
|
+
interface BluetoothPairingDetails {
|
|
4
|
+
deviceId: string;
|
|
5
|
+
pairingKind: 'confirm' | 'confirmPin' | 'providePin';
|
|
6
|
+
pin?: string;
|
|
7
|
+
}
|
|
8
|
+
interface BluetoothPairingResponse {
|
|
9
|
+
confirmed: boolean;
|
|
10
|
+
pin?: string;
|
|
11
|
+
}
|
|
12
|
+
interface BluetoothDevice {
|
|
13
|
+
deviceId: string;
|
|
14
|
+
deviceName: string | null;
|
|
15
|
+
}
|
|
16
|
+
interface BleDevice {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
paired?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface BleEnumerateOptions {
|
|
22
|
+
timeout?: number;
|
|
23
|
+
forceScan?: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface ElectronBleAPI {
|
|
26
|
+
enumerate(options?: BleEnumerateOptions): Promise<BleDevice[]>;
|
|
27
|
+
onBleSelect(callback: (devices: BleDevice[]) => void): () => void;
|
|
28
|
+
selectBleDevice(deviceId: string): void;
|
|
29
|
+
cancelBleRequest(): void;
|
|
30
|
+
onBlePairingRequest(callback: (details: BluetoothPairingDetails) => void): () => void;
|
|
31
|
+
respondToPairing(response: BluetoothPairingResponse): void;
|
|
32
|
+
onBleDeviceDisconnected(callback: (device: {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string | null;
|
|
35
|
+
}) => void): () => void;
|
|
36
|
+
}
|
|
37
|
+
declare const IPC_CHANNELS: {
|
|
38
|
+
readonly BLE_ENUMERATE: "ble-enumerate";
|
|
39
|
+
readonly BLE_ENUMERATE_RESULT: "ble-enumerate-result";
|
|
40
|
+
readonly BLE_SCAN_TIMEOUT: "ble-scan-timeout";
|
|
41
|
+
readonly BLE_SELECT: "ble-select";
|
|
42
|
+
readonly BLE_SELECT_RESULT: "ble-select-result";
|
|
43
|
+
readonly BLE_PAIRING_REQUEST: "ble-pairing-request";
|
|
44
|
+
readonly BLE_PAIRING_RESPONSE: "ble-pairing-response";
|
|
45
|
+
};
|
|
46
|
+
declare global {
|
|
47
|
+
interface Window {
|
|
48
|
+
electronBleAPI: ElectronBleAPI;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare function initElectronBleBridge(webContents: WebContents): void;
|
|
53
|
+
|
|
54
|
+
export { BleDevice, BleEnumerateOptions, BluetoothDevice, BluetoothPairingDetails, BluetoothPairingResponse, ElectronBleAPI, IPC_CHANNELS, initElectronBleBridge };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,UAAU,CAAC;AAGjE,cAAc,SAAS,CAAC;AAmBxB,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,QAmK7D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var hdShared = require('@onekeyfe/hd-shared');
|
|
6
|
+
|
|
7
|
+
const IPC_CHANNELS = {
|
|
8
|
+
BLE_ENUMERATE: 'ble-enumerate',
|
|
9
|
+
BLE_ENUMERATE_RESULT: 'ble-enumerate-result',
|
|
10
|
+
BLE_SCAN_TIMEOUT: 'ble-scan-timeout',
|
|
11
|
+
BLE_SELECT: 'ble-select',
|
|
12
|
+
BLE_SELECT_RESULT: 'ble-select-result',
|
|
13
|
+
BLE_PAIRING_REQUEST: 'ble-pairing-request',
|
|
14
|
+
BLE_PAIRING_RESPONSE: 'ble-pairing-response',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function initElectronBleBridge(webContents) {
|
|
18
|
+
const electron = require('electron');
|
|
19
|
+
const { ipcMain } = electron;
|
|
20
|
+
const logger = require('electron-log');
|
|
21
|
+
const { session } = webContents;
|
|
22
|
+
let selectBluetoothCallback = null;
|
|
23
|
+
let bluetoothPairingCallback = null;
|
|
24
|
+
let preSelectedDeviceId = null;
|
|
25
|
+
let lastLoggedDeviceList = '';
|
|
26
|
+
const logDeviceListIfChanged = (deviceList) => {
|
|
27
|
+
const sortedDevices = [...deviceList].sort((a, b) => a.deviceId.localeCompare(b.deviceId));
|
|
28
|
+
const deviceListString = JSON.stringify(sortedDevices.map(d => ({
|
|
29
|
+
deviceId: d.deviceId,
|
|
30
|
+
deviceName: d.deviceName,
|
|
31
|
+
})));
|
|
32
|
+
if (deviceListString !== lastLoggedDeviceList) {
|
|
33
|
+
logger.info('[Main] Raw device list:', deviceList);
|
|
34
|
+
lastLoggedDeviceList = deviceListString;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
logger.info('[Main] select-bluetooth-device event triggered');
|
|
42
|
+
const deviceListChanged = logDeviceListIfChanged(deviceList);
|
|
43
|
+
if (!deviceListChanged) {
|
|
44
|
+
logger.info('[Main] Device list unchanged, continuing...');
|
|
45
|
+
}
|
|
46
|
+
logger.info('[Main] Pre-selected device:', preSelectedDeviceId);
|
|
47
|
+
selectBluetoothCallback = callback;
|
|
48
|
+
if (!deviceList.length) {
|
|
49
|
+
logger.info('[Main] Empty device list, waiting for more devices...');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const filteredDevices = deviceList
|
|
53
|
+
.filter(d => hdShared.isOnekeyDevice(d.deviceName))
|
|
54
|
+
.map(d => ({ id: d.deviceId, name: d.deviceName }));
|
|
55
|
+
if (filteredDevices.length > 0) {
|
|
56
|
+
if (deviceListChanged) {
|
|
57
|
+
logger.info('[Main] Found OneKey devices:', filteredDevices);
|
|
58
|
+
}
|
|
59
|
+
if (preSelectedDeviceId) {
|
|
60
|
+
logger.info('[Main] Set Pre-selected device:', preSelectedDeviceId);
|
|
61
|
+
const targetDevice = filteredDevices.find(d => d.id === preSelectedDeviceId);
|
|
62
|
+
if (targetDevice) {
|
|
63
|
+
logger.info('[Main] Found pre-selected device:', targetDevice);
|
|
64
|
+
callback(targetDevice.id);
|
|
65
|
+
selectBluetoothCallback = null;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
webContents.send(hdShared.EOneKeyBleMessageKeys.BLE_SELECT, filteredDevices);
|
|
70
|
+
}
|
|
71
|
+
else if (deviceListChanged) {
|
|
72
|
+
logger.info('[Main] No OneKey devices in this batch, continue scanning...');
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
ipcMain.on(hdShared.EOneKeyBleMessageKeys.BLE_PRE_SELECT, (_event, deviceId) => {
|
|
76
|
+
logger.info('[Main] Pre-selecting device:', deviceId);
|
|
77
|
+
preSelectedDeviceId = deviceId;
|
|
78
|
+
lastLoggedDeviceList = '';
|
|
79
|
+
});
|
|
80
|
+
ipcMain.on(hdShared.EOneKeyBleMessageKeys.BLE_CLEAR_PRE_SELECT, () => {
|
|
81
|
+
logger.info('[Main] Clearing pre-selected device');
|
|
82
|
+
preSelectedDeviceId = null;
|
|
83
|
+
lastLoggedDeviceList = '';
|
|
84
|
+
});
|
|
85
|
+
ipcMain.on(hdShared.EOneKeyBleMessageKeys.BLE_SELECT_RESULT, (_event, deviceId) => {
|
|
86
|
+
logger.info('[Main] Received ble-select-result:', deviceId);
|
|
87
|
+
if (selectBluetoothCallback) {
|
|
88
|
+
selectBluetoothCallback(deviceId || '');
|
|
89
|
+
selectBluetoothCallback = null;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
ipcMain.on(hdShared.EOneKeyBleMessageKeys.BLE_CANCEL_REQUEST, () => {
|
|
93
|
+
logger.info('[Main] Received cancel-bluetooth-request');
|
|
94
|
+
if (selectBluetoothCallback) {
|
|
95
|
+
selectBluetoothCallback('');
|
|
96
|
+
selectBluetoothCallback = null;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
ipcMain.on(hdShared.EOneKeyBleMessageKeys.BLE_STOP_SCAN, () => {
|
|
100
|
+
logger.info('[Main] Received stop BLE scan request');
|
|
101
|
+
if (selectBluetoothCallback) {
|
|
102
|
+
selectBluetoothCallback('');
|
|
103
|
+
selectBluetoothCallback = null;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
session.setBluetoothPairingHandler((details, callback) => {
|
|
107
|
+
logger.info('[Main] Bluetooth pairing request received:', details);
|
|
108
|
+
logger.info('[Main] Pairing device ID:', details.deviceId);
|
|
109
|
+
logger.info('[Main] Pairing type:', details.pairingKind);
|
|
110
|
+
bluetoothPairingCallback = callback;
|
|
111
|
+
webContents.send(hdShared.EOneKeyBleMessageKeys.BLE_PAIRING_REQUEST, details);
|
|
112
|
+
});
|
|
113
|
+
logger.info('[Main] Bluetooth pairing handler registered');
|
|
114
|
+
ipcMain.on(hdShared.EOneKeyBleMessageKeys.BLE_PAIRING_RESPONSE, (_event, response) => {
|
|
115
|
+
logger.info('[Main] Received pairing response:', response);
|
|
116
|
+
if (bluetoothPairingCallback) {
|
|
117
|
+
bluetoothPairingCallback(response);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
exports.IPC_CHANNELS = IPC_CHANNELS;
|
|
123
|
+
exports.initElectronBleBridge = initElectronBleBridge;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface BluetoothPairingDetails {
|
|
2
|
+
deviceId: string;
|
|
3
|
+
pairingKind: 'confirm' | 'confirmPin' | 'providePin';
|
|
4
|
+
pin?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface BluetoothPairingResponse {
|
|
7
|
+
confirmed: boolean;
|
|
8
|
+
pin?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface BluetoothDevice {
|
|
11
|
+
deviceId: string;
|
|
12
|
+
deviceName: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface BleDevice {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
paired?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface BleEnumerateOptions {
|
|
20
|
+
timeout?: number;
|
|
21
|
+
forceScan?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ElectronBleAPI {
|
|
24
|
+
enumerate(options?: BleEnumerateOptions): Promise<BleDevice[]>;
|
|
25
|
+
onBleSelect(callback: (devices: BleDevice[]) => void): () => void;
|
|
26
|
+
selectBleDevice(deviceId: string): void;
|
|
27
|
+
cancelBleRequest(): void;
|
|
28
|
+
onBlePairingRequest(callback: (details: BluetoothPairingDetails) => void): () => void;
|
|
29
|
+
respondToPairing(response: BluetoothPairingResponse): void;
|
|
30
|
+
onBleDeviceDisconnected(callback: (device: {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string | null;
|
|
33
|
+
}) => void): () => void;
|
|
34
|
+
}
|
|
35
|
+
export declare const IPC_CHANNELS: {
|
|
36
|
+
readonly BLE_ENUMERATE: "ble-enumerate";
|
|
37
|
+
readonly BLE_ENUMERATE_RESULT: "ble-enumerate-result";
|
|
38
|
+
readonly BLE_SCAN_TIMEOUT: "ble-scan-timeout";
|
|
39
|
+
readonly BLE_SELECT: "ble-select";
|
|
40
|
+
readonly BLE_SELECT_RESULT: "ble-select-result";
|
|
41
|
+
readonly BLE_PAIRING_REQUEST: "ble-pairing-request";
|
|
42
|
+
readonly BLE_PAIRING_RESPONSE: "ble-pairing-response";
|
|
43
|
+
};
|
|
44
|
+
declare global {
|
|
45
|
+
interface Window {
|
|
46
|
+
electronBleAPI: ElectronBleAPI;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,SAAS,GAAG,YAAY,GAAG,YAAY,CAAC;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAIlC,OAAO,CAAC,EAAE,MAAM,CAAC;IAIjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAE7B,SAAS,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAG/D,WAAW,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,gBAAgB,IAAI,IAAI,CAAC;IAGzB,mBAAmB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACtF,gBAAgB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAG3D,uBAAuB,CACrB,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,GAC9D,MAAM,IAAI,CAAC;CACf;AAGD,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAEX,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,cAAc,EAAE,cAAc,CAAC;KAChC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onekeyfe/hd-transport-electron",
|
|
3
|
+
"version": "1.0.34-alpha.0",
|
|
4
|
+
"author": "OneKey",
|
|
5
|
+
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/OneKeyHQ/hardware-js-sdk.git"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w",
|
|
18
|
+
"build": "rimraf dist && rollup -c ../../build/rollup.config.js",
|
|
19
|
+
"lint": "eslint .",
|
|
20
|
+
"lint:fix": "eslint . --fix"
|
|
21
|
+
},
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@onekeyfe/hd-transport-web-device": "^1.0.28",
|
|
25
|
+
"electron": ">=25.0.0",
|
|
26
|
+
"electron-log": ">=4.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@onekeyfe/hd-shared": "^1.0.34-alpha.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/web-bluetooth": "^0.0.17",
|
|
33
|
+
"electron": "^25.0.0",
|
|
34
|
+
"typescript": "^5.3.3"
|
|
35
|
+
},
|
|
36
|
+
"gitHead": "a910879dea6198bd96016aec81429f31b9692bf1"
|
|
37
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Electron BLE bridge helper
|
|
3
|
+
* Running in Main process only. At runtime it dynamically requires 'electron',
|
|
4
|
+
* so projects that don't use Electron will tree-shake this file out and won't bundle electron.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/ban-ts-comment, import/no-extraneous-dependencies, global-require, import/no-unresolved */
|
|
8
|
+
|
|
9
|
+
import { isOnekeyDevice, EOneKeyBleMessageKeys } from '@onekeyfe/hd-shared';
|
|
10
|
+
import type { WebContents, IpcMainEvent, Event } from 'electron';
|
|
11
|
+
|
|
12
|
+
// Export all type definitions
|
|
13
|
+
export * from './types';
|
|
14
|
+
|
|
15
|
+
// Internal interface definitions
|
|
16
|
+
interface BluetoothDevice {
|
|
17
|
+
deviceId: string;
|
|
18
|
+
deviceName: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface BluetoothPairingDetails {
|
|
22
|
+
deviceId: string;
|
|
23
|
+
pairingKind: 'confirm' | 'confirmPin' | 'providePin';
|
|
24
|
+
pin?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type BluetoothPairingResponse = {
|
|
28
|
+
confirmed: boolean;
|
|
29
|
+
pin?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function initElectronBleBridge(webContents: WebContents) {
|
|
33
|
+
// @ts-ignore – electron is only available at runtime within an Electron app
|
|
34
|
+
const electron = require('electron');
|
|
35
|
+
const { ipcMain } = electron;
|
|
36
|
+
|
|
37
|
+
// @ts-ignore – electron-log is only available at runtime within an Electron app
|
|
38
|
+
const logger = require('electron-log');
|
|
39
|
+
|
|
40
|
+
const { session } = webContents;
|
|
41
|
+
|
|
42
|
+
let selectBluetoothCallback: ((deviceId: string) => void) | null = null;
|
|
43
|
+
let bluetoothPairingCallback: ((response: BluetoothPairingResponse) => void) | null = null;
|
|
44
|
+
let preSelectedDeviceId: string | null = null;
|
|
45
|
+
|
|
46
|
+
// Track last logged device list to avoid duplicate logs
|
|
47
|
+
let lastLoggedDeviceList = '';
|
|
48
|
+
|
|
49
|
+
// Helper function to log device list only if changed
|
|
50
|
+
const logDeviceListIfChanged = (deviceList: BluetoothDevice[]): boolean => {
|
|
51
|
+
// Sort devices by deviceId to ensure consistent comparison regardless of order
|
|
52
|
+
const sortedDevices = [...deviceList].sort((a, b) => a.deviceId.localeCompare(b.deviceId));
|
|
53
|
+
const deviceListString = JSON.stringify(
|
|
54
|
+
sortedDevices.map(d => ({
|
|
55
|
+
deviceId: d.deviceId,
|
|
56
|
+
deviceName: d.deviceName,
|
|
57
|
+
}))
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Only log if the device list has changed
|
|
61
|
+
if (deviceListString !== lastLoggedDeviceList) {
|
|
62
|
+
logger.info('[Main] Raw device list:', deviceList);
|
|
63
|
+
lastLoggedDeviceList = deviceListString;
|
|
64
|
+
return true; // Logged
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false; // Not logged (duplicate)
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// 1️⃣ Device selection events
|
|
71
|
+
webContents.on(
|
|
72
|
+
'select-bluetooth-device',
|
|
73
|
+
(event: Event, deviceList: BluetoothDevice[], callback: (deviceId: string) => void) => {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
|
|
76
|
+
logger.info('[Main] select-bluetooth-device event triggered');
|
|
77
|
+
|
|
78
|
+
// Use helper function to log device list only if changed
|
|
79
|
+
const deviceListChanged = logDeviceListIfChanged(deviceList);
|
|
80
|
+
|
|
81
|
+
if (!deviceListChanged) {
|
|
82
|
+
// If device list hasn't changed, just log a simple message
|
|
83
|
+
logger.info('[Main] Device list unchanged, continuing...');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
logger.info('[Main] Pre-selected device:', preSelectedDeviceId);
|
|
87
|
+
|
|
88
|
+
// Save callback for later use
|
|
89
|
+
selectBluetoothCallback = callback;
|
|
90
|
+
|
|
91
|
+
// If device list is empty, wait for next event
|
|
92
|
+
if (!deviceList.length) {
|
|
93
|
+
logger.info('[Main] Empty device list, waiting for more devices...');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Filter OneKey devices and send to renderer process
|
|
98
|
+
const filteredDevices = deviceList
|
|
99
|
+
.filter(d => isOnekeyDevice(d.deviceName))
|
|
100
|
+
.map(d => ({ id: d.deviceId, name: d.deviceName }));
|
|
101
|
+
|
|
102
|
+
if (filteredDevices.length > 0) {
|
|
103
|
+
// Only log found devices if the device list actually changed
|
|
104
|
+
if (deviceListChanged) {
|
|
105
|
+
logger.info('[Main] Found OneKey devices:', filteredDevices);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// If there's a pre-selected device, select it directly
|
|
109
|
+
if (preSelectedDeviceId) {
|
|
110
|
+
logger.info('[Main] Set Pre-selected device:', preSelectedDeviceId);
|
|
111
|
+
const targetDevice = filteredDevices.find(d => d.id === preSelectedDeviceId);
|
|
112
|
+
if (targetDevice) {
|
|
113
|
+
logger.info('[Main] Found pre-selected device:', targetDevice);
|
|
114
|
+
callback(targetDevice.id);
|
|
115
|
+
selectBluetoothCallback = null;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Continuously send newly discovered devices
|
|
121
|
+
webContents.send(EOneKeyBleMessageKeys.BLE_SELECT, filteredDevices);
|
|
122
|
+
} else if (deviceListChanged) {
|
|
123
|
+
logger.info('[Main] No OneKey devices in this batch, continue scanning...');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Device pre-selection related
|
|
129
|
+
ipcMain.on(EOneKeyBleMessageKeys.BLE_PRE_SELECT, (_event: IpcMainEvent, deviceId: string) => {
|
|
130
|
+
logger.info('[Main] Pre-selecting device:', deviceId);
|
|
131
|
+
preSelectedDeviceId = deviceId;
|
|
132
|
+
// Reset device list tracking when pre-selecting
|
|
133
|
+
lastLoggedDeviceList = '';
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
ipcMain.on(EOneKeyBleMessageKeys.BLE_CLEAR_PRE_SELECT, () => {
|
|
137
|
+
logger.info('[Main] Clearing pre-selected device');
|
|
138
|
+
preSelectedDeviceId = null;
|
|
139
|
+
// Reset device list tracking when clearing pre-selection
|
|
140
|
+
lastLoggedDeviceList = '';
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Renderer process returns selection result
|
|
144
|
+
ipcMain.on(EOneKeyBleMessageKeys.BLE_SELECT_RESULT, (_event: IpcMainEvent, deviceId?: string) => {
|
|
145
|
+
logger.info('[Main] Received ble-select-result:', deviceId);
|
|
146
|
+
if (selectBluetoothCallback) {
|
|
147
|
+
selectBluetoothCallback(deviceId || '');
|
|
148
|
+
selectBluetoothCallback = null;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Allow user to cancel
|
|
153
|
+
ipcMain.on(EOneKeyBleMessageKeys.BLE_CANCEL_REQUEST, () => {
|
|
154
|
+
logger.info('[Main] Received cancel-bluetooth-request');
|
|
155
|
+
if (selectBluetoothCallback) {
|
|
156
|
+
selectBluetoothCallback('');
|
|
157
|
+
selectBluetoothCallback = null;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Handle stop scan requests
|
|
162
|
+
ipcMain.on(EOneKeyBleMessageKeys.BLE_STOP_SCAN, () => {
|
|
163
|
+
logger.info('[Main] Received stop BLE scan request');
|
|
164
|
+
if (selectBluetoothCallback) {
|
|
165
|
+
selectBluetoothCallback('');
|
|
166
|
+
selectBluetoothCallback = null;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// 2️⃣ PIN / Confirm handling
|
|
171
|
+
session.setBluetoothPairingHandler(
|
|
172
|
+
(details: BluetoothPairingDetails, callback: (response: BluetoothPairingResponse) => void) => {
|
|
173
|
+
logger.info('[Main] Bluetooth pairing request received:', details);
|
|
174
|
+
logger.info('[Main] Pairing device ID:', details.deviceId);
|
|
175
|
+
logger.info('[Main] Pairing type:', details.pairingKind);
|
|
176
|
+
bluetoothPairingCallback = callback;
|
|
177
|
+
webContents.send(EOneKeyBleMessageKeys.BLE_PAIRING_REQUEST, details);
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
logger.info('[Main] Bluetooth pairing handler registered');
|
|
181
|
+
|
|
182
|
+
// 3️⃣ Device disconnect handling
|
|
183
|
+
// Note: Electron doesn't provide a direct bluetooth-device-disconnected event
|
|
184
|
+
// Device disconnection should be handled at the Web Bluetooth API level in the renderer process
|
|
185
|
+
|
|
186
|
+
ipcMain.on(
|
|
187
|
+
EOneKeyBleMessageKeys.BLE_PAIRING_RESPONSE,
|
|
188
|
+
(_event: IpcMainEvent, response: BluetoothPairingResponse) => {
|
|
189
|
+
logger.info('[Main] Received pairing response:', response);
|
|
190
|
+
if (bluetoothPairingCallback) {
|
|
191
|
+
bluetoothPairingCallback(response);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export interface BluetoothPairingDetails {
|
|
2
|
+
deviceId: string;
|
|
3
|
+
pairingKind: 'confirm' | 'confirmPin' | 'providePin';
|
|
4
|
+
pin?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface BluetoothPairingResponse {
|
|
8
|
+
confirmed: boolean;
|
|
9
|
+
pin?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BluetoothDevice {
|
|
13
|
+
deviceId: string;
|
|
14
|
+
deviceName: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface BleDevice {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
paired?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface BleEnumerateOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Scan timeout in milliseconds (default: 30s)
|
|
26
|
+
*/
|
|
27
|
+
timeout?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Whether to force a new scan even if paired devices exist
|
|
30
|
+
*/
|
|
31
|
+
forceScan?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ElectronBleAPI {
|
|
35
|
+
// Device enumeration
|
|
36
|
+
enumerate(options?: BleEnumerateOptions): Promise<BleDevice[]>;
|
|
37
|
+
|
|
38
|
+
// Device selection
|
|
39
|
+
onBleSelect(callback: (devices: BleDevice[]) => void): () => void;
|
|
40
|
+
selectBleDevice(deviceId: string): void;
|
|
41
|
+
cancelBleRequest(): void;
|
|
42
|
+
|
|
43
|
+
// Device pairing
|
|
44
|
+
onBlePairingRequest(callback: (details: BluetoothPairingDetails) => void): () => void;
|
|
45
|
+
respondToPairing(response: BluetoothPairingResponse): void;
|
|
46
|
+
|
|
47
|
+
// Device disconnection
|
|
48
|
+
onBleDeviceDisconnected(
|
|
49
|
+
callback: (device: { id: string; name: string | null }) => void
|
|
50
|
+
): () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// IPC Channel Names
|
|
54
|
+
export const IPC_CHANNELS = {
|
|
55
|
+
BLE_ENUMERATE: 'ble-enumerate',
|
|
56
|
+
BLE_ENUMERATE_RESULT: 'ble-enumerate-result',
|
|
57
|
+
BLE_SCAN_TIMEOUT: 'ble-scan-timeout',
|
|
58
|
+
BLE_SELECT: 'ble-select',
|
|
59
|
+
BLE_SELECT_RESULT: 'ble-select-result',
|
|
60
|
+
BLE_PAIRING_REQUEST: 'ble-pairing-request',
|
|
61
|
+
BLE_PAIRING_RESPONSE: 'ble-pairing-response',
|
|
62
|
+
} as const;
|
|
63
|
+
|
|
64
|
+
declare global {
|
|
65
|
+
interface Window {
|
|
66
|
+
electronBleAPI: ElectronBleAPI;
|
|
67
|
+
}
|
|
68
|
+
}
|