* fix(device): check privilege on Linux * device connected if also have permission * fix rules sh path * refactor permission detection * fic hasPermission condition * fix return value
189 lines
5.9 KiB
TypeScript
189 lines
5.9 KiB
TypeScript
import { ipcMain } from 'electron';
|
|
import { ConfigurationReply, DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
|
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
|
import { Observable } from 'rxjs/Observable';
|
|
import { Subscription } from 'rxjs/Subscription';
|
|
import { emptyDir } from 'fs-extra';
|
|
|
|
import 'rxjs/add/observable/interval';
|
|
import 'rxjs/add/operator/startWith';
|
|
import 'rxjs/add/operator/map';
|
|
import 'rxjs/add/operator/do';
|
|
import 'rxjs/add/operator/distinctUntilChanged';
|
|
|
|
import { saveTmpFirmware } from '../util/save-extract-firmware';
|
|
import { TmpFirmware } from '../models/tmp-firmware';
|
|
import { QueueManager } from './queue-manager';
|
|
|
|
/**
|
|
* IpcMain pair of the UHK Communication
|
|
* Functionality:
|
|
* - Send UserConfiguration to the UHK Device
|
|
* - Read UserConfiguration from the UHK Device
|
|
*/
|
|
export class DeviceService {
|
|
private pollTimer$: Subscription;
|
|
private queueManager = new QueueManager();
|
|
|
|
constructor(private logService: LogService,
|
|
private win: Electron.BrowserWindow,
|
|
private device: UhkHidDevice,
|
|
private operations: UhkOperations) {
|
|
this.pollUhkDevice();
|
|
|
|
ipcMain.on(IpcEvents.device.saveUserConfiguration, (...args: any[]) => {
|
|
this.queueManager.add({
|
|
method: this.saveUserConfiguration,
|
|
bind: this,
|
|
params: args,
|
|
asynchronous: true
|
|
});
|
|
});
|
|
|
|
ipcMain.on(IpcEvents.device.loadConfigurations, (...args: any[]) => {
|
|
this.queueManager.add({
|
|
method: this.loadConfigurations,
|
|
bind: this,
|
|
params: args,
|
|
asynchronous: true
|
|
});
|
|
});
|
|
|
|
ipcMain.on(IpcEvents.device.updateFirmware, (...args: any[]) => {
|
|
this.queueManager.add({
|
|
method: this.updateFirmware,
|
|
bind: this,
|
|
params: args,
|
|
asynchronous: true
|
|
});
|
|
});
|
|
|
|
ipcMain.on(IpcEvents.device.startConnectionPoller, this.pollUhkDevice.bind(this));
|
|
|
|
logService.debug('[DeviceService] init success');
|
|
}
|
|
|
|
/**
|
|
* Return with the actual UserConfiguration from UHK Device
|
|
* @returns {Promise<Buffer>}
|
|
*/
|
|
public async loadConfigurations(event: Electron.Event): Promise<void> {
|
|
let response: ConfigurationReply;
|
|
|
|
try {
|
|
await this.device.waitUntilKeyboardBusy();
|
|
const result = await this.operations.loadConfigurations();
|
|
|
|
response = {
|
|
success: true,
|
|
...result
|
|
};
|
|
} catch (error) {
|
|
response = {
|
|
success: false,
|
|
error: error.message
|
|
};
|
|
} finally {
|
|
this.device.close();
|
|
}
|
|
|
|
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
|
|
}
|
|
|
|
public close(): void {
|
|
this.stopPollTimer();
|
|
this.logService.info('[DeviceService] Device connection checker stopped.');
|
|
}
|
|
|
|
public async updateFirmware(event: Electron.Event, args?: Array<string>): Promise<void> {
|
|
const response = new IpcResponse();
|
|
let firmwarePathData: TmpFirmware;
|
|
|
|
try {
|
|
this.stopPollTimer();
|
|
|
|
if (args && args.length > 0) {
|
|
firmwarePathData = await saveTmpFirmware(args[0]);
|
|
await this.operations.updateRightFirmware(firmwarePathData.rightFirmwarePath);
|
|
await this.operations.updateLeftModule(firmwarePathData.leftFirmwarePath);
|
|
}
|
|
else {
|
|
await this.operations.updateRightFirmware();
|
|
await this.operations.updateLeftModule();
|
|
}
|
|
|
|
response.success = true;
|
|
} catch (error) {
|
|
const err = {message: error.message, stack: error.stack};
|
|
this.logService.error('[DeviceService] updateFirmware error', err);
|
|
|
|
response.error = err;
|
|
}
|
|
|
|
if (firmwarePathData) {
|
|
await emptyDir(firmwarePathData.tmpDirectory.name);
|
|
}
|
|
|
|
await snooze(500);
|
|
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
|
}
|
|
|
|
/**
|
|
* HID API not support device attached and detached event.
|
|
* This method check the keyboard is attached to the computer or not.
|
|
* Every second check the HID device list.
|
|
* @private
|
|
*/
|
|
private pollUhkDevice(): void {
|
|
if (this.pollTimer$) {
|
|
return;
|
|
}
|
|
|
|
this.pollTimer$ = Observable.interval(1000)
|
|
.startWith(0)
|
|
.map(() => this.device.deviceConnected())
|
|
.distinctUntilChanged()
|
|
.do((connected: boolean) => {
|
|
const response: DeviceConnectionState = {
|
|
connected,
|
|
hasPermission: this.device.hasPermission()
|
|
};
|
|
|
|
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
|
|
this.logService.info('[DeviceService] Device connection state changed to:', response);
|
|
})
|
|
.subscribe();
|
|
}
|
|
|
|
private async saveUserConfiguration(event: Electron.Event, args: Array<string>): Promise<void> {
|
|
const response = new IpcResponse();
|
|
const json = args[0];
|
|
|
|
try {
|
|
await this.operations.saveUserConfiguration(json);
|
|
|
|
response.success = true;
|
|
}
|
|
catch (error) {
|
|
this.logService.error('[DeviceService] Transferring error', error);
|
|
response.error = {message: error.message};
|
|
} finally {
|
|
this.device.close();
|
|
}
|
|
|
|
event.sender.send(IpcEvents.device.saveUserConfigurationReply, response);
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
private stopPollTimer(): void {
|
|
if (!this.pollTimer$) {
|
|
return;
|
|
}
|
|
|
|
this.pollTimer$.unsubscribe();
|
|
this.pollTimer$ = null;
|
|
|
|
}
|
|
}
|