使用node-hid类库,实现打开指定usb设备,并回显数据的功能。
开发环境版本记录
开发环境 mac电脑
nodejs ,版本16.18.0
electron ,版本21.4.4
electron-builder ,版本23.6.0
ee-bin ,版本 1.4.0
ee-core,版本 2.9.1
better-sqlite3,版本 v8.7.0
此次新增
pyenv ,管理python版本,当前选择 python 2.7.8 (本机还安装有python 3.12.2)
node-hid ,版本 3.0.0
官网参考
实现步骤
vue页面
只截取关键部分
<script setup>
import { ipcApiRoute } from '@/api/main';
import { ipc } from '@/utils/ipcRenderer';
import { toRaw, onMounted, ref, reactive } from 'vue';
const HID = require('node-hid');
const defaultDeviceName = ref('')
const usbList = ref([]);
const chooseDevice = reactive({
data:
{
"interface": 1,
"manufacturer": "i22222",
"path": "DevSrvsID:222",
"product": "keyboard",
"productId": 222,
"release": 8192,
"serialNumber": "233333",
"usagePage": 2,
"vendorId": 3
}
});
onMounted(() => {
init();
})
const init = () => {
// 避免重复监听,或者将 on 功能写到一个统一的地方,只加载一次
ipc.removeAllListeners(ipcApiRoute.usbReciveData);
ipc.on(ipcApiRoute.usbReciveData, (event, result) => {
console.log('usbReciveData', result);
})
ipc.removeAllListeners(ipcApiRoute.usbOnerror);
ipc.on(ipcApiRoute.usbOnerror, (event, result) => {
console.log('usbOnerror', result);
})
}
const clearUsbhidHard = () => {
chooseDevice.data = {};
usbList.value = [];
}
const getUsbhidHard = () => {
ipc.invoke(ipcApiRoute.getUsbhidDeviceList, {}).then(res => {
usbList.value = res;
console.log('result', res);
if (res.length > 0) {
chooseDevice.data = { ...res[0] }
}
})
}
const openDevice = () => {
const params = {
productId: chooseDevice.data.productId,
vendorId: chooseDevice.data.vendorId,
}
ipc.send(ipcApiRoute.openDeviceByIds, params)
}
const closeDevice = () => {
ipc.invoke(ipcApiRoute.closeUsbDevice, {}).then(res => {
console.log('关闭', res);
})
}
const sendToDevice = () => {
ipc.invoke(ipcApiRoute.sendDataToUsb, { data: null }).then(res => {
console.log('发送数据', res);
})
}
</script>
controll部分
路径: electron/controller/usbhidHardware.js
'use strict';
const { Controller } = require('ee-core');
const path = require('path');
const Ps = require('ee-core/ps');
const CoreWindow = require('ee-core/electron/window');
const Addon = require('ee-core/addon');
const HID = require('node-hid');
const Log = require('ee-core/log');
var os = require('os');
const Services = require('ee-core/services');
/**
* 硬件设备 - usbhid通讯
* @class
*/
class UsbhidHardwareController extends Controller {
constructor(ctx) {
super(ctx);
this.device = null;
}
/**
* 获取Usb列表
* @param {*} args = {name}
* @returns list
*/
getUsbhidDeviceList() {
//主线程获取usb列表
const list = HID.devices();
return list;
}
/**
* 根据productId, vendorId打开设备,
* @param {*} args = { productId, vendorId }
* @returns deviceHandle
*/
async openDeviceByIds(args, event) {
try {
//this.closeUsbDevice();
Log.info('openDeviceByIds args', args)
const { productId, vendorId } = args;
// const device = new HID.HID(vendorId, productId);
this.device = await HID.HIDAsync.open(vendorId, productId);
Log.info("打开设备的句柄, handler");
Log.info(this.device);
Log.info("Attaching receive 'data' handler");
if (!this.device) {
Log.info("打开失败");
return false;
} else {
Log.info("打开成功");
Log.info(await this.device.getDeviceInfo());
}
this.device.on('data', function (data) {
try {
if (data, event) {
const stringRecv = data.toString('hex');
Log.info("==111==== Got data:", stringRecv);
// Log.info(event)
const channel = 'controller.usbhidHardware.usbReciveData';
Log.info("==222====" + channel)
event.reply(`${channel}`, { data: stringRecv });
}
} catch (error) {
Log.error("error:", error);
}
});
this.device.on('error', function (err) {
try {
Log.error("error:", err);
const channel = 'controller.usbhidHardware.usbOnerror';
if (event) {
Log.info("==222====" + channel)
event.reply(`${channel}`, { data: err });
}
this.device.close()
} catch (error) {
}
});
return true;
} catch (error) {
Log.error("error:", error);
}
}
sendDataToUsb(args, event) {
try {
Log.info('sendDataToUsb args', args)
const { data } = args;
if (this.device) {
const buf = [];
buf[0] = 0x0A; //report id
buf[1] = 4; //bytes, data length (包括自己)
buf[2] = 0xA0;// dongle to pc data.
buf[3] = 0; //
buf[4] = 0; //
Log.info('Sending message B: ', JSON.stringify(buf))
// write output report with report id 0x0A
const numsentB = this.device.write(buf);
Log.info('messageB len:', buf.length, 'actual sent len:', numsentB);
return true;
}
return false;
} catch (error) {
Log.error("error:", error);
return false;
}
}
closeUsbDevice(args, event) {
try {
Log.info('closeUsbDevice args', args)
if (this.device) {
this.device.close();
// this.device = null;
return true;
}
return false;
} catch (error) {
Log.error("error:", error);
return false;
}
}
}
UsbhidHardwareController.toString = () => '[class UsbhidHardwareController]';
module.exports = UsbhidHardwareController;
service部分
路径: electron/service/usbhidHardware.js
'use strict';
const { Service } = require('ee-core');
/**
* hardware(service层为单例)
* @class
*/
class UsbhidHardwareService extends Service {
constructor(ctx) {
super(ctx);
}
isTeensy(d, args) {
const { productId, vendorId } = args;
return d.vendorId === vendorId && d.productId === productId;
}
isRawHidUsage(d, args) {
const { usagePage, usage } = args;
return (d.usagePage === usagePage && d.usage === usage);
}
}
UsbhidHardwareService.toString = () => '[class UsbhidHardwareService]';
module.exports = UsbhidHardwareService;
main.js映射部分
getUsbhidDeviceList: 'controller.usbhidHardware.getUsbhidDeviceList',
openDeviceByIds: 'controller.usbhidHardware.openDeviceByIds',
usbReciveData: 'controller.usbhidHardware.usbReciveData',
usbOnerror: 'controller.usbhidHardware.usbOnerror',
closeUsbDevice: 'controller.usbhidHardware.closeUsbDevice',
sendDataToUsb: 'controller.usbhidHardware.sendDataToUsb',
遗留问题
现在会出现 应用程序退出不了,需要强制退出的情况。device.close()貌似退出不了。 有时候出现设备连接不了的情况,需要插拔设备并重启开关。 哪位伙伴知晓问题的话,拜托回来指点指点我呗。
参考文章
electron和无线鼠标通信代码 - 追逐繁星的孩子的文章 - 知乎 zhuanlan.zhihu.com/p/633818295