feat: device recovery mode (#642)
* add new page and ipc processing * refactor: remove unused references from uhk.js * feat: add device recovery route * refactor: device permission * feat: write firmware update log to the screen * fix: xterm height * feat: add reload button to the recovery page * refactor: deviceConnectionState.hasPermission in appStartInfo * refactor: use correct imports * refactor: move .ok-button css class to the main style.scss * feat: add bootload active route guard * style: move RecoveryDeviceAction into new line * feat: delete reload button * feat: start device polling after device recovery
This commit is contained in:
committed by
László Monda
parent
2cf8044987
commit
653465f0e0
3
package-lock.json
generated
3
package-lock.json
generated
@@ -5838,7 +5838,8 @@
|
|||||||
"jsbn": {
|
"jsbn": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"json-schema": {
|
"json-schema": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.3",
|
||||||
|
|||||||
@@ -22,13 +22,14 @@ export class AppService extends MainServiceBase {
|
|||||||
|
|
||||||
private async handleAppStartInfo(event: Electron.Event) {
|
private async handleAppStartInfo(event: Electron.Event) {
|
||||||
this.logService.info('[AppService] getAppStartInfo');
|
this.logService.info('[AppService] getAppStartInfo');
|
||||||
|
const deviceConnectionState = this.uhkHidDeviceService.getDeviceConnectionState();
|
||||||
const response: AppStartInfo = {
|
const response: AppStartInfo = {
|
||||||
commandLineArgs: {
|
commandLineArgs: {
|
||||||
addons: this.options.addons || false
|
addons: this.options.addons || false
|
||||||
},
|
},
|
||||||
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
|
deviceConnected: deviceConnectionState.connected,
|
||||||
hasPermission: this.uhkHidDeviceService.hasPermission()
|
hasPermission: deviceConnectionState.hasPermission,
|
||||||
|
bootloaderActive: deviceConnectionState.bootloaderActive
|
||||||
};
|
};
|
||||||
this.logService.info('[AppService] getAppStartInfo response:', response);
|
this.logService.info('[AppService] getAppStartInfo response:', response);
|
||||||
return event.sender.send(IpcEvents.app.getAppStartInfoReply, response);
|
return event.sender.send(IpcEvents.app.getAppStartInfoReply, response);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
mapObjectToUserConfigBinaryBuffer,
|
mapObjectToUserConfigBinaryBuffer,
|
||||||
SaveUserConfigurationData
|
SaveUserConfigurationData
|
||||||
} from 'uhk-common';
|
} from 'uhk-common';
|
||||||
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
import { deviceConnectionStateComparer, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
import { emptyDir } from 'fs-extra';
|
import { emptyDir } from 'fs-extra';
|
||||||
@@ -71,6 +71,15 @@ export class DeviceService {
|
|||||||
|
|
||||||
ipcMain.on(IpcEvents.device.startConnectionPoller, this.pollUhkDevice.bind(this));
|
ipcMain.on(IpcEvents.device.startConnectionPoller, this.pollUhkDevice.bind(this));
|
||||||
|
|
||||||
|
ipcMain.on(IpcEvents.device.recoveryDevice, (...args: any[]) => {
|
||||||
|
this.queueManager.add({
|
||||||
|
method: this.recoveryDevice,
|
||||||
|
bind: this,
|
||||||
|
params: args,
|
||||||
|
asynchronous: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
logService.debug('[DeviceService] init success');
|
logService.debug('[DeviceService] init success');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +157,29 @@ export class DeviceService {
|
|||||||
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async recoveryDevice(event: Electron.Event): Promise<void> {
|
||||||
|
const response = new IpcResponse();
|
||||||
|
try {
|
||||||
|
this.stopPollTimer();
|
||||||
|
|
||||||
|
await this.operations.updateRightFirmware();
|
||||||
|
|
||||||
|
await snooze(500);
|
||||||
|
|
||||||
|
this.pollUhkDevice();
|
||||||
|
|
||||||
|
response.success = true;
|
||||||
|
} catch (error) {
|
||||||
|
const err = {message: error.message, stack: error.stack};
|
||||||
|
this.logService.error('[DeviceService] updateFirmware error', err);
|
||||||
|
|
||||||
|
response.error = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
await snooze(500);
|
||||||
|
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HID API not support device attached and detached event.
|
* HID API not support device attached and detached event.
|
||||||
* This method check the keyboard is attached to the computer or not.
|
* This method check the keyboard is attached to the computer or not.
|
||||||
@@ -161,16 +193,11 @@ export class DeviceService {
|
|||||||
|
|
||||||
this.pollTimer$ = Observable.interval(1000)
|
this.pollTimer$ = Observable.interval(1000)
|
||||||
.startWith(0)
|
.startWith(0)
|
||||||
.map(() => this.device.deviceConnected())
|
.map(() => this.device.getDeviceConnectionState())
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged<DeviceConnectionState>(deviceConnectionStateComparer)
|
||||||
.do((connected: boolean) => {
|
.do((state: DeviceConnectionState) => {
|
||||||
const response: DeviceConnectionState = {
|
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
|
||||||
connected,
|
this.logService.info('[DeviceService] Device connection state changed to:', state);
|
||||||
hasPermission: this.device.hasPermission()
|
|
||||||
};
|
|
||||||
|
|
||||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
|
|
||||||
this.logService.info('[DeviceService] Device connection state changed to:', response);
|
|
||||||
})
|
})
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ export interface AppStartInfo {
|
|||||||
commandLineArgs: CommandLineArgs;
|
commandLineArgs: CommandLineArgs;
|
||||||
deviceConnected: boolean;
|
deviceConnected: boolean;
|
||||||
hasPermission: boolean;
|
hasPermission: boolean;
|
||||||
|
bootloaderActive: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export interface DeviceConnectionState {
|
export interface DeviceConnectionState {
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
hasPermission: boolean;
|
hasPermission: boolean;
|
||||||
|
bootloaderActive: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export class Device {
|
|||||||
public static readonly updateFirmware = 'device-update-firmware';
|
public static readonly updateFirmware = 'device-update-firmware';
|
||||||
public static readonly updateFirmwareReply = 'device-update-firmware-reply';
|
public static readonly updateFirmwareReply = 'device-update-firmware-reply';
|
||||||
public static readonly startConnectionPoller = 'device-start-connection-poller';
|
public static readonly startConnectionPoller = 'device-start-connection-poller';
|
||||||
|
public static readonly recoveryDevice = 'device-recovery';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IpcEvents {
|
export class IpcEvents {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export namespace Constants {
|
export namespace Constants {
|
||||||
export const VENDOR_ID = 0x1D50;
|
export const VENDOR_ID = 0x1D50;
|
||||||
export const PRODUCT_ID = 0x6122;
|
export const PRODUCT_ID = 0x6122;
|
||||||
|
export const BOOTLOADER_ID = 0x6120;
|
||||||
export const MAX_PAYLOAD_SIZE = 64;
|
export const MAX_PAYLOAD_SIZE = 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { cloneDeep, isEqual } from 'lodash';
|
import { cloneDeep, isEqual } from 'lodash';
|
||||||
import { Device, devices, HID } from 'node-hid';
|
import { Device, devices, HID } from 'node-hid';
|
||||||
import { CommandLineArgs, LogService } from 'uhk-common';
|
import { CommandLineArgs, DeviceConnectionState, LogService } from 'uhk-common';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ConfigBufferId,
|
ConfigBufferId,
|
||||||
@@ -50,12 +50,10 @@ export class UhkHidDevice {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.deviceConnected()) {
|
const devs = devices();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._hasPermission = this.getDevice() !== null;
|
this._hasPermission = devs.some((x: Device) => x.vendorId === Constants.VENDOR_ID &&
|
||||||
this.close();
|
(x.productId === Constants.PRODUCT_ID || x.productId === Constants.BOOTLOADER_ID));
|
||||||
|
|
||||||
return this._hasPermission;
|
return this._hasPermission;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -69,15 +67,25 @@ export class UhkHidDevice {
|
|||||||
* Return with true is an UHK Device is connected to the computer.
|
* Return with true is an UHK Device is connected to the computer.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
public deviceConnected(): boolean {
|
public getDeviceConnectionState(): DeviceConnectionState {
|
||||||
const connected = devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID &&
|
const devs = devices();
|
||||||
dev.productId === Constants.PRODUCT_ID);
|
const result: DeviceConnectionState = {
|
||||||
|
bootloaderActive: false,
|
||||||
|
connected: false,
|
||||||
|
hasPermission: this.hasPermission()
|
||||||
|
};
|
||||||
|
|
||||||
if (!connected) {
|
for (const dev of devs) {
|
||||||
this._hasPermission = false;
|
if (dev.vendorId === Constants.VENDOR_ID &&
|
||||||
|
dev.productId === Constants.PRODUCT_ID) {
|
||||||
|
result.connected = true;
|
||||||
|
} else if (dev.vendorId === Constants.VENDOR_ID &&
|
||||||
|
dev.productId === Constants.BOOTLOADER_ID) {
|
||||||
|
result.bootloaderActive = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return connected;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Constants, UsbCommand } from './constants';
|
import { Constants, UsbCommand } from './constants';
|
||||||
import { LogService } from 'uhk-common';
|
import { DeviceConnectionState, LogService } from 'uhk-common';
|
||||||
|
|
||||||
export const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
|
export const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
@@ -95,3 +95,9 @@ export async function retry(command: Function, maxTry = 3, logService?: LogServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const deviceConnectionStateComparer = (a: DeviceConnectionState, b: DeviceConnectionState): boolean => {
|
||||||
|
return a.hasPermission === b.hasPermission
|
||||||
|
&& a.connected === b.connected
|
||||||
|
&& a.bootloaderActive === b.bootloaderActive;
|
||||||
|
};
|
||||||
|
|||||||
@@ -5,17 +5,19 @@ import { deviceRoutes } from './components/device';
|
|||||||
import { addOnRoutes } from './components/add-on';
|
import { addOnRoutes } from './components/add-on';
|
||||||
import { keymapRoutes } from './components/keymap';
|
import { keymapRoutes } from './components/keymap';
|
||||||
import { macroRoutes } from './components/macro';
|
import { macroRoutes } from './components/macro';
|
||||||
import { PrivilegeCheckerComponent } from './components/privilege-checker/privilege-checker.component';
|
import { PrivilegeCheckerComponent } from './components/privilege-checker';
|
||||||
import { MissingDeviceComponent } from './components/missing-device/missing-device.component';
|
import { MissingDeviceComponent } from './components/missing-device';
|
||||||
import { UhkDeviceDisconnectedGuard } from './services/uhk-device-disconnected.guard';
|
import { UhkDeviceDisconnectedGuard } from './services/uhk-device-disconnected.guard';
|
||||||
import { UhkDeviceConnectedGuard } from './services/uhk-device-connected.guard';
|
import { UhkDeviceConnectedGuard } from './services/uhk-device-connected.guard';
|
||||||
import { UhkDeviceUninitializedGuard } from './services/uhk-device-uninitialized.guard';
|
import { UhkDeviceUninitializedGuard } from './services/uhk-device-uninitialized.guard';
|
||||||
import { UhkDeviceInitializedGuard } from './services/uhk-device-initialized.guard';
|
import { UhkDeviceInitializedGuard } from './services/uhk-device-initialized.guard';
|
||||||
import { MainPage } from './pages/main-page/main.page';
|
import { MainPage } from './pages/main-page/main.page';
|
||||||
import { agentRoutes } from './components/agent/agent.routes';
|
import { agentRoutes } from './components/agent';
|
||||||
import { LoadingDevicePageComponent } from './pages/loading-page/loading-device.page';
|
import { LoadingDevicePageComponent } from './pages/loading-page/loading-device.page';
|
||||||
import { UhkDeviceLoadingGuard } from './services/uhk-device-loading.guard';
|
import { UhkDeviceLoadingGuard } from './services/uhk-device-loading.guard';
|
||||||
import { UhkDeviceLoadedGuard } from './services/uhk-device-loaded.guard';
|
import { UhkDeviceLoadedGuard } from './services/uhk-device-loaded.guard';
|
||||||
|
import { RecoveryModeComponent } from './components/device';
|
||||||
|
import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloader-not-active.guard';
|
||||||
|
|
||||||
const appRoutes: Routes = [
|
const appRoutes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -33,6 +35,11 @@ const appRoutes: Routes = [
|
|||||||
component: LoadingDevicePageComponent,
|
component: LoadingDevicePageComponent,
|
||||||
canActivate: [UhkDeviceLoadedGuard]
|
canActivate: [UhkDeviceLoadedGuard]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'recovery-device',
|
||||||
|
component: RecoveryModeComponent,
|
||||||
|
canActivate: [UhkDeviceBootloaderNotActiveGuard]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: MainPage,
|
component: MainPage,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { DeviceFirmwareComponent } from './firmware/device-firmware.component';
|
|||||||
import { MouseSpeedComponent } from './mouse-speed/mouse-speed.component';
|
import { MouseSpeedComponent } from './mouse-speed/mouse-speed.component';
|
||||||
import { LEDBrightnessComponent } from './led-brightness/led-brightness.component';
|
import { LEDBrightnessComponent } from './led-brightness/led-brightness.component';
|
||||||
import { RestoreConfigurationComponent } from './restore-configuration/restore-configuration.component';
|
import { RestoreConfigurationComponent } from './restore-configuration/restore-configuration.component';
|
||||||
|
import { RecoveryModeComponent } from './recovery-mode/recovery-mode.component';
|
||||||
|
|
||||||
export const deviceRoutes: Routes = [
|
export const deviceRoutes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -34,6 +35,10 @@ export const deviceRoutes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'restore-user-configuration',
|
path: 'restore-user-configuration',
|
||||||
component: RestoreConfigurationComponent
|
component: RestoreConfigurationComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'recovery-mode',
|
||||||
|
component: RecoveryModeComponent
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<div class="flex-grow" #scrollMe>
|
<div class="flex-grow" #scrollMe>
|
||||||
<xterm [logs]="xtermLog$ | async"></xterm>
|
<xterm [logs]="xtermLog$ | async"></xterm>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="flex-footer">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-primary ok-button"
|
class="btn btn-primary ok-button"
|
||||||
[disabled]="firmwareOkButtonDisabled$ | async"
|
[disabled]="firmwareOkButtonDisabled$ | async"
|
||||||
|
|||||||
@@ -5,25 +5,3 @@
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-container {
|
|
||||||
height: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-grow {
|
|
||||||
background-color: black;
|
|
||||||
overflow: auto;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
margin-top: 0.5em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ok-button {
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ export * from './firmware/device-firmware.component';
|
|||||||
export * from './mouse-speed/mouse-speed.component';
|
export * from './mouse-speed/mouse-speed.component';
|
||||||
export * from './led-brightness/led-brightness.component';
|
export * from './led-brightness/led-brightness.component';
|
||||||
export * from './restore-configuration/restore-configuration.component';
|
export * from './restore-configuration/restore-configuration.component';
|
||||||
|
export * from './recovery-mode/recovery-mode.component';
|
||||||
export * from './device.routes';
|
export * from './device.routes';
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<div class="full-height">
|
||||||
|
<div class="flex-container">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<h1>
|
||||||
|
<i class="fa fa-wrench"></i>
|
||||||
|
<span>Fix device</span>
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
Your device seems to be broken. No worries, Agent can fix it.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
[disabled]="flashFirmwareButtonDisbabled$ | async"
|
||||||
|
(click)="onRecoveryDevice()">Fix device
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow" #scrollMe>
|
||||||
|
<xterm [logs]="xtermLog"></xterm>
|
||||||
|
</div>
|
||||||
|
<div class="flex-footer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
:host {
|
||||||
|
overflow-y: auto;
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
|
import { XtermLog } from '../../../models/xterm-log';
|
||||||
|
import { AppState, flashFirmwareButtonDisbabled, xtermLog } from '../../../store';
|
||||||
|
import { RecoveryDeviceAction } from '../../../store/actions/device';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'device-recovery-mode',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
templateUrl: './recovery-mode.component.html',
|
||||||
|
styleUrls: ['./recovery-mode.component.scss'],
|
||||||
|
host: {
|
||||||
|
'class': 'container-fluid'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class RecoveryModeComponent implements OnInit, OnDestroy {
|
||||||
|
xtermLogSubscription: Subscription;
|
||||||
|
flashFirmwareButtonDisbabled$: Observable<boolean>;
|
||||||
|
|
||||||
|
xtermLog: Array<XtermLog>;
|
||||||
|
|
||||||
|
@ViewChild('scrollMe') divElement: ElementRef;
|
||||||
|
|
||||||
|
constructor(private store: Store<AppState>,
|
||||||
|
private cdRef: ChangeDetectorRef) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.flashFirmwareButtonDisbabled$ = this.store.select(flashFirmwareButtonDisbabled);
|
||||||
|
this.xtermLogSubscription = this.store.select(xtermLog)
|
||||||
|
.subscribe(data => {
|
||||||
|
this.xtermLog = data;
|
||||||
|
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
|
if (this.divElement && this.divElement.nativeElement) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.divElement.nativeElement.scrollTop = this.divElement.nativeElement.scrollHeight;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.xtermLogSubscription) {
|
||||||
|
this.xtermLogSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecoveryDevice(): void {
|
||||||
|
this.store.dispatch(new RecoveryDeviceAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,10 @@ export class DeviceRendererService {
|
|||||||
this.ipcRenderer.send(IpcEvents.device.startConnectionPoller);
|
this.ipcRenderer.send(IpcEvents.device.startConnectionPoller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recoveryDevice(): void {
|
||||||
|
this.ipcRenderer.send(IpcEvents.device.recoveryDevice);
|
||||||
|
}
|
||||||
|
|
||||||
private registerEvents(): void {
|
private registerEvents(): void {
|
||||||
this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: DeviceConnectionState) => {
|
this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: DeviceConnectionState) => {
|
||||||
this.dispachStoreAction(new ConnectionStateChangedAction(arg));
|
this.dispachStoreAction(new ConnectionStateChangedAction(arg));
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { CanActivate, Router } from '@angular/router';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import 'rxjs/add/operator/do';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
|
||||||
|
import { AppState, bootloaderActive } from '../store';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UhkDeviceBootloaderNotActiveGuard implements CanActivate {
|
||||||
|
|
||||||
|
constructor(private store: Store<AppState>, private router: Router) { }
|
||||||
|
|
||||||
|
canActivate(): Observable<boolean> {
|
||||||
|
return this.store.select(bootloaderActive)
|
||||||
|
.do(active => {
|
||||||
|
if (!active) {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,8 @@ import {
|
|||||||
DeviceFirmwareComponent,
|
DeviceFirmwareComponent,
|
||||||
MouseSpeedComponent,
|
MouseSpeedComponent,
|
||||||
LEDBrightnessComponent,
|
LEDBrightnessComponent,
|
||||||
RestoreConfigurationComponent
|
RestoreConfigurationComponent,
|
||||||
|
RecoveryModeComponent
|
||||||
} from './components/device';
|
} from './components/device';
|
||||||
import { KeymapAddComponent, KeymapEditComponent, KeymapHeaderComponent } from './components/keymap';
|
import { KeymapAddComponent, KeymapEditComponent, KeymapHeaderComponent } from './components/keymap';
|
||||||
import { LayersComponent } from './components/layers';
|
import { LayersComponent } from './components/layers';
|
||||||
@@ -105,6 +106,7 @@ import { XtermComponent } from './components/xterm/xterm.component';
|
|||||||
import { SliderWrapperComponent } from './components/slider-wrapper/slider-wrapper.component';
|
import { SliderWrapperComponent } from './components/slider-wrapper/slider-wrapper.component';
|
||||||
import { EditableTextComponent } from './components/editable-text/editable-text.component';
|
import { EditableTextComponent } from './components/editable-text/editable-text.component';
|
||||||
import { Autofocus } from './directives/autofocus/autofocus.directive';
|
import { Autofocus } from './directives/autofocus/autofocus.directive';
|
||||||
|
import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloader-not-active.guard';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -176,7 +178,8 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
|
|||||||
SliderWrapperComponent,
|
SliderWrapperComponent,
|
||||||
EditableTextComponent,
|
EditableTextComponent,
|
||||||
Autofocus,
|
Autofocus,
|
||||||
RestoreConfigurationComponent
|
RestoreConfigurationComponent,
|
||||||
|
RecoveryModeComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -211,7 +214,8 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
|
|||||||
UhkDeviceInitializedGuard,
|
UhkDeviceInitializedGuard,
|
||||||
UhkDeviceUninitializedGuard,
|
UhkDeviceUninitializedGuard,
|
||||||
UhkDeviceLoadingGuard,
|
UhkDeviceLoadingGuard,
|
||||||
UhkDeviceLoadedGuard
|
UhkDeviceLoadedGuard,
|
||||||
|
UhkDeviceBootloaderNotActiveGuard
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
UhkMessageComponent,
|
UhkMessageComponent,
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export const ActionTypes = {
|
|||||||
MODULES_INFO_LOADED: type(PREFIX + 'module info loaded'),
|
MODULES_INFO_LOADED: type(PREFIX + 'module info loaded'),
|
||||||
HAS_BACKUP_USER_CONFIGURATION: type(PREFIX + 'Store backup user configuration'),
|
HAS_BACKUP_USER_CONFIGURATION: type(PREFIX + 'Store backup user configuration'),
|
||||||
RESTORE_CONFIGURATION_FROM_BACKUP: type(PREFIX + 'Restore configuration from backup'),
|
RESTORE_CONFIGURATION_FROM_BACKUP: type(PREFIX + 'Restore configuration from backup'),
|
||||||
RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS: type(PREFIX + 'Restore configuration from backup success')
|
RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS: type(PREFIX + 'Restore configuration from backup success'),
|
||||||
|
RECOVERY_DEVICE: type(PREFIX + 'Recovery device')
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SetPrivilegeOnLinuxAction implements Action {
|
export class SetPrivilegeOnLinuxAction implements Action {
|
||||||
@@ -140,6 +141,10 @@ export class RestoreUserConfigurationFromBackupSuccessAction implements Action {
|
|||||||
type = ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS;
|
type = ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class RecoveryDeviceAction implements Action {
|
||||||
|
type = ActionTypes.RECOVERY_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
export type Actions
|
export type Actions
|
||||||
= SetPrivilegeOnLinuxAction
|
= SetPrivilegeOnLinuxAction
|
||||||
| SetPrivilegeOnLinuxReplyAction
|
| SetPrivilegeOnLinuxReplyAction
|
||||||
@@ -162,4 +167,5 @@ export type Actions
|
|||||||
| RestoreUserConfigurationFromBackupAction
|
| RestoreUserConfigurationFromBackupAction
|
||||||
| HasBackupUserConfigurationAction
|
| HasBackupUserConfigurationAction
|
||||||
| RestoreUserConfigurationFromBackupSuccessAction
|
| RestoreUserConfigurationFromBackupSuccessAction
|
||||||
|
| RecoveryDeviceAction
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ export class ApplicationEffects {
|
|||||||
new ApplyCommandLineArgsAction(appInfo.commandLineArgs),
|
new ApplyCommandLineArgsAction(appInfo.commandLineArgs),
|
||||||
new ConnectionStateChangedAction({
|
new ConnectionStateChangedAction({
|
||||||
connected: appInfo.deviceConnected,
|
connected: appInfo.deviceConnected,
|
||||||
hasPermission: appInfo.hasPermission
|
hasPermission: appInfo.hasPermission,
|
||||||
|
bootloaderActive: appInfo.bootloaderActive
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
ActionTypes,
|
ActionTypes,
|
||||||
ConnectionStateChangedAction,
|
ConnectionStateChangedAction,
|
||||||
HideSaveToKeyboardButton,
|
HideSaveToKeyboardButton,
|
||||||
|
RecoveryDeviceAction,
|
||||||
ResetUserConfigurationAction,
|
ResetUserConfigurationAction,
|
||||||
RestoreUserConfigurationFromBackupSuccessAction,
|
RestoreUserConfigurationFromBackupSuccessAction,
|
||||||
SaveConfigurationAction,
|
SaveConfigurationAction,
|
||||||
@@ -60,6 +61,9 @@ export class DeviceEffects {
|
|||||||
if (!state.hasPermission) {
|
if (!state.hasPermission) {
|
||||||
this.router.navigate(['/privilege']);
|
this.router.navigate(['/privilege']);
|
||||||
}
|
}
|
||||||
|
else if (state.bootloaderActive) {
|
||||||
|
this.router.navigate(['/recovery-device']);
|
||||||
|
}
|
||||||
else if (state.connected) {
|
else if (state.connected) {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
@@ -90,7 +94,8 @@ export class DeviceEffects {
|
|||||||
if (response.success) {
|
if (response.success) {
|
||||||
return new ConnectionStateChangedAction({
|
return new ConnectionStateChangedAction({
|
||||||
connected: true,
|
connected: true,
|
||||||
hasPermission: true
|
hasPermission: true,
|
||||||
|
bootloaderActive: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +219,10 @@ export class DeviceEffects {
|
|||||||
.ofType<ResetUserConfigurationAction>(ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP)
|
.ofType<ResetUserConfigurationAction>(ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP)
|
||||||
.map(() => new SaveConfigurationAction());
|
.map(() => new SaveConfigurationAction());
|
||||||
|
|
||||||
|
@Effect({dispatch: false}) recoveryDevice$ = this.actions$
|
||||||
|
.ofType<RecoveryDeviceAction>(ActionTypes.RECOVERY_DEVICE)
|
||||||
|
.do(() => this.deviceRendererService.recoveryDevice());
|
||||||
|
|
||||||
constructor(private actions$: Actions,
|
constructor(private actions$: Actions,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private deviceRendererService: DeviceRendererService,
|
private deviceRendererService: DeviceRendererService,
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export const flashFirmwareButtonDisbabled = createSelector(runningInElectron, de
|
|||||||
export const getHardwareModules = createSelector(deviceState, fromDevice.getHardwareModules);
|
export const getHardwareModules = createSelector(deviceState, fromDevice.getHardwareModules);
|
||||||
export const getBackupUserConfigurationState = createSelector(deviceState, fromDevice.getBackupUserConfigurationState);
|
export const getBackupUserConfigurationState = createSelector(deviceState, fromDevice.getBackupUserConfigurationState);
|
||||||
export const getRestoreUserConfiguration = createSelector(deviceState, fromDevice.getHasBackupUserConfiguration);
|
export const getRestoreUserConfiguration = createSelector(deviceState, fromDevice.getHasBackupUserConfiguration);
|
||||||
|
export const bootloaderActive = createSelector(deviceState, fromDevice.bootloaderActive);
|
||||||
|
|
||||||
export const getSideMenuPageState = createSelector(
|
export const getSideMenuPageState = createSelector(
|
||||||
showAddonMenu,
|
showAddonMenu,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { RestoreConfigurationState } from '../../models/restore-configuration-st
|
|||||||
export interface State {
|
export interface State {
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
hasPermission: boolean;
|
hasPermission: boolean;
|
||||||
|
bootloaderActive: boolean;
|
||||||
saveToKeyboard: ProgressButtonState;
|
saveToKeyboard: ProgressButtonState;
|
||||||
updatingFirmware: boolean;
|
updatingFirmware: boolean;
|
||||||
firmwareUpdateFinished: boolean;
|
firmwareUpdateFinished: boolean;
|
||||||
@@ -29,6 +30,7 @@ export interface State {
|
|||||||
export const initialState: State = {
|
export const initialState: State = {
|
||||||
connected: true,
|
connected: true,
|
||||||
hasPermission: true,
|
hasPermission: true,
|
||||||
|
bootloaderActive: false,
|
||||||
saveToKeyboard: initProgressButtonState,
|
saveToKeyboard: initProgressButtonState,
|
||||||
updatingFirmware: false,
|
updatingFirmware: false,
|
||||||
firmwareUpdateFinished: false,
|
firmwareUpdateFinished: false,
|
||||||
@@ -53,7 +55,8 @@ export function reducer(state = initialState, action: Action) {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
connected: data.connected,
|
connected: data.connected,
|
||||||
hasPermission: data.hasPermission
|
hasPermission: data.hasPermission,
|
||||||
|
bootloaderActive: data.bootloaderActive
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +196,13 @@ export function reducer(state = initialState, action: Action) {
|
|||||||
hasBackupUserConfiguration: false
|
hasBackupUserConfiguration: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case ActionTypes.RECOVERY_DEVICE: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updatingFirmware: true,
|
||||||
|
log: [{message: '', cssClass: XtermCssClass.standard}]
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -212,3 +222,4 @@ export const getBackupUserConfigurationState = (state: State): RestoreConfigurat
|
|||||||
hasBackupUserConfiguration: state.hasBackupUserConfiguration
|
hasBackupUserConfiguration: state.hasBackupUserConfiguration
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
export const bootloaderActive = (state: State) => state.bootloaderActive;
|
||||||
|
|||||||
@@ -155,3 +155,25 @@ pre {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-container {
|
||||||
|
height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-grow {
|
||||||
|
background-color: black;
|
||||||
|
overflow: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-footer {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ok-button {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
const util = require('util');
|
|
||||||
const HID = require('node-hid');
|
const HID = require('node-hid');
|
||||||
const {HardwareConfiguration, UhkBuffer} = require('uhk-common');
|
const {HardwareConfiguration, UhkBuffer} = require('uhk-common');
|
||||||
const {getTransferBuffers, ConfigBufferId, UhkHidDevice, UsbCommand} = require('uhk-usb');
|
|
||||||
const Logger = require('./logger');
|
const Logger = require('./logger');
|
||||||
const debug = process.env.DEBUG;
|
const debug = process.env.DEBUG;
|
||||||
|
|
||||||
@@ -18,7 +16,7 @@ const kbootCommandIdToName = {
|
|||||||
const eepromOperationIdToName = {
|
const eepromOperationIdToName = {
|
||||||
0: 'read',
|
0: 'read',
|
||||||
1: 'write',
|
1: 'write',
|
||||||
}
|
};
|
||||||
|
|
||||||
function bufferToString(buffer) {
|
function bufferToString(buffer) {
|
||||||
let str = '';
|
let str = '';
|
||||||
|
|||||||
Reference in New Issue
Block a user