Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bb645125d | ||
|
|
9471b31a5d | ||
|
|
ffa52757c9 | ||
|
|
ee53a0df9b | ||
|
|
8e20c85e07 | ||
|
|
65ea786358 | ||
|
|
1035837b3b | ||
|
|
18fc2e6b3f | ||
|
|
fc728697d7 | ||
|
|
cdf3caee9e | ||
|
|
0a4d3a002e | ||
|
|
d11c532ea4 | ||
|
|
1ff51697b1 | ||
|
|
ab8ae31324 | ||
|
|
daa0e723b1 | ||
|
|
609aba856a | ||
|
|
a6678bd537 | ||
|
|
6c4f580fc2 | ||
|
|
ea41661c65 | ||
|
|
c553c7b63b | ||
|
|
e5988aa800 | ||
|
|
ae319c607f | ||
|
|
5d23ad1c9e | ||
|
|
55eef50da7 | ||
|
|
653465f0e0 | ||
|
|
2cf8044987 | ||
|
|
3c056a7255 | ||
|
|
091796d13c | ||
|
|
eb97dd844f | ||
|
|
17693ec8fe | ||
|
|
7c7ce8f50f |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,9 +6,24 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1
|
|||||||
|
|
||||||
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
|
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
|
## [1.2.2] - 2018-05-27
|
||||||
|
|
||||||
|
Firmware: 8.2.**5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.**1** | User Config: 4.0.**1** | Hardware Config: 1.0.0
|
||||||
|
|
||||||
|
- Offer recovery for bricked right keyboard halfs.
|
||||||
|
- Detect when the hardware configuration of a device is invalid and display a notification.
|
||||||
|
- Check if the keyboard is in factory reset mode and if so, display a relevant instruction.
|
||||||
|
- Only allow ASCII characters in type text macro actions.
|
||||||
|
- Allow uploading the same file multiple times in a row.
|
||||||
|
- Only send auto update notification when the user initiates the update.
|
||||||
|
- Update the firmware versions on the firmware update page right after firmware updates.
|
||||||
|
- Add a lot of useful instructions to the firmware page to help users update the firmware.
|
||||||
|
- Add the operating system and initial device list to the firmware update log.
|
||||||
|
- Add copy to clipboard button to the top right corner of the firmware update terminal widget.
|
||||||
|
|
||||||
## [1.2.1] - 2018-05-12
|
## [1.2.1] - 2018-05-12
|
||||||
|
|
||||||
Firmware: 8.2.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.2)] | Device Protocol: 4.3.0| User Config: 4.0.**1** | Hardware Config: 1.0.0
|
Firmware: 8.2.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.2)] | Device Protocol: 4.3.0 | User Config: 4.0.**1** | Hardware Config: 1.0.0
|
||||||
|
|
||||||
- Match for the new USB usage page and usage number. This is critical for UHKs flashed with firmware >=8.2.2 to be recognized by Agent on OSX.
|
- Match for the new USB usage page and usage number. This is critical for UHKs flashed with firmware >=8.2.2 to be recognized by Agent on OSX.
|
||||||
- Make the config serializer handle long media macro actions. `USERCONFIG:PATCH`
|
- Make the config serializer handle long media macro actions. `USERCONFIG:PATCH`
|
||||||
|
|||||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uhk-agent",
|
"name": "uhk-agent",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"author": "Ultimate Gadget Laboratories",
|
"author": "Ultimate Gadget Laboratories",
|
||||||
"main": "electron/dist/electron-main.js",
|
"main": "electron/dist/electron-main.js",
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"firmwareVersion": "8.2.2",
|
"firmwareVersion": "8.2.5",
|
||||||
"deviceProtocolVersion": "4.3.0",
|
"deviceProtocolVersion": "4.3.1",
|
||||||
"userConfigVersion": "4.0.1",
|
"userConfigVersion": "4.0.1",
|
||||||
"hardwareConfigVersion": "1.0.0",
|
"hardwareConfigVersion": "1.0.0",
|
||||||
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
|
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import { IpcEvents, LogService } from 'uhk-common';
|
|||||||
import { MainServiceBase } from './main-service-base';
|
import { MainServiceBase } from './main-service-base';
|
||||||
|
|
||||||
export class AppUpdateService extends MainServiceBase {
|
export class AppUpdateService extends MainServiceBase {
|
||||||
|
|
||||||
|
private sendAutoUpdateNotification = false;
|
||||||
|
|
||||||
constructor(protected logService: LogService,
|
constructor(protected logService: LogService,
|
||||||
protected win: Electron.BrowserWindow,
|
protected win: Electron.BrowserWindow,
|
||||||
private app: Electron.App) {
|
private app: Electron.App) {
|
||||||
@@ -24,16 +27,21 @@ export class AppUpdateService extends MainServiceBase {
|
|||||||
|
|
||||||
private initListeners() {
|
private initListeners() {
|
||||||
autoUpdater.on('checking-for-update', () => {
|
autoUpdater.on('checking-for-update', () => {
|
||||||
|
this.logService.debug('[AppUpdateService] checking for update');
|
||||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkingForUpdate);
|
this.sendIpcToWindow(IpcEvents.autoUpdater.checkingForUpdate);
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-available', async (ev: any, info: UpdateInfo) => {
|
autoUpdater.on('update-available', async (ev: any, info: UpdateInfo) => {
|
||||||
|
this.logService.debug('[AppUpdateService] update available. Downloading started');
|
||||||
await autoUpdater.downloadUpdate();
|
await autoUpdater.downloadUpdate();
|
||||||
this.sendIpcToWindow(IpcEvents.autoUpdater.updateAvailable, info);
|
this.sendIpcToWindow(IpcEvents.autoUpdater.updateAvailable, info);
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-not-available', (ev: any, info: UpdateInfo) => {
|
autoUpdater.on('update-not-available', (ev: any, info: UpdateInfo) => {
|
||||||
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
|
if (this.sendAutoUpdateNotification) {
|
||||||
|
this.logService.debug('[AppUpdateService] update not available');
|
||||||
|
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('error', (ev: any, err: string) => {
|
autoUpdater.on('error', (ev: any, err: string) => {
|
||||||
@@ -51,6 +59,7 @@ export class AppUpdateService extends MainServiceBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-downloaded', (ev: any, info: UpdateInfo) => {
|
autoUpdater.on('update-downloaded', (ev: any, info: UpdateInfo) => {
|
||||||
|
this.logService.debug('[AppUpdateService] update downloaded');
|
||||||
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateDownloaded, info);
|
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateDownloaded, info);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -61,12 +70,15 @@ export class AppUpdateService extends MainServiceBase {
|
|||||||
|
|
||||||
ipcMain.on(IpcEvents.app.appStarted, () => {
|
ipcMain.on(IpcEvents.app.appStarted, () => {
|
||||||
if (this.checkForUpdateAtStartup()) {
|
if (this.checkForUpdateAtStartup()) {
|
||||||
|
this.sendAutoUpdateNotification = false;
|
||||||
|
this.logService.debug('[AppUpdateService] app started. Automatically check for update.');
|
||||||
this.checkForUpdate();
|
this.checkForUpdate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => {
|
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => {
|
||||||
this.logService.debug('[AppUpdateService] checkForUpdate request from renderer process');
|
this.logService.debug('[AppUpdateService] checkForUpdate request from renderer process');
|
||||||
|
this.sendAutoUpdateNotification = true;
|
||||||
this.checkForUpdate();
|
this.checkForUpdate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -75,14 +87,22 @@ export class AppUpdateService extends MainServiceBase {
|
|||||||
if (isDev) {
|
if (isDev) {
|
||||||
const msg = '[AppUpdateService] Application update is not working in dev mode.';
|
const msg = '[AppUpdateService] Application update is not working in dev mode.';
|
||||||
this.logService.info(msg);
|
this.logService.info(msg);
|
||||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
|
||||||
|
if (this.sendAutoUpdateNotification) {
|
||||||
|
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isFirstRun()) {
|
if (this.isFirstRun()) {
|
||||||
const msg = '[AppUpdateService] Application update is skipping at first run.';
|
const msg = '[AppUpdateService] Application update is skipping at first run.';
|
||||||
this.logService.info(msg);
|
this.logService.info(msg);
|
||||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
|
||||||
|
if (this.sendAutoUpdateNotification) {
|
||||||
|
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ipcMain } from 'electron';
|
|||||||
import {
|
import {
|
||||||
ConfigurationReply,
|
ConfigurationReply,
|
||||||
DeviceConnectionState,
|
DeviceConnectionState,
|
||||||
|
FirmwareUpgradeIpcResponse,
|
||||||
getHardwareConfigFromDeviceResponse,
|
getHardwareConfigFromDeviceResponse,
|
||||||
HardwareModules,
|
HardwareModules,
|
||||||
IpcEvents,
|
IpcEvents,
|
||||||
@@ -10,7 +11,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 +72,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');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,10 +94,7 @@ export class DeviceService {
|
|||||||
try {
|
try {
|
||||||
await this.device.waitUntilKeyboardBusy();
|
await this.device.waitUntilKeyboardBusy();
|
||||||
const result = await this.operations.loadConfigurations();
|
const result = await this.operations.loadConfigurations();
|
||||||
const modules: HardwareModules = {
|
const modules: HardwareModules = await this.getHardwareModules(false);
|
||||||
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
|
|
||||||
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
|
|
||||||
};
|
|
||||||
|
|
||||||
const hardwareConfig = getHardwareConfigFromDeviceResponse(result.hardwareConfiguration);
|
const hardwareConfig = getHardwareConfigFromDeviceResponse(result.hardwareConfiguration);
|
||||||
const uniqueId = hardwareConfig.uniqueId;
|
const uniqueId = hardwareConfig.uniqueId;
|
||||||
@@ -110,16 +117,36 @@ export class DeviceService {
|
|||||||
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
|
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getHardwareModules(catchError: boolean): Promise<HardwareModules> {
|
||||||
|
try {
|
||||||
|
await this.device.waitUntilKeyboardBusy();
|
||||||
|
|
||||||
|
return {
|
||||||
|
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
|
||||||
|
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (!catchError) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logService.error('[DeviceService] Read hardware modules information failed', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public close(): void {
|
public close(): void {
|
||||||
this.stopPollTimer();
|
this.stopPollTimer();
|
||||||
this.logService.info('[DeviceService] Device connection checker stopped.');
|
this.logService.info('[DeviceService] Device connection checker stopped.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateFirmware(event: Electron.Event, args?: Array<string>): Promise<void> {
|
public async updateFirmware(event: Electron.Event, args?: Array<string>): Promise<void> {
|
||||||
const response = new IpcResponse();
|
const response = new FirmwareUpgradeIpcResponse();
|
||||||
|
|
||||||
let firmwarePathData: TmpFirmware;
|
let firmwarePathData: TmpFirmware;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.device.resetDeviceCache();
|
||||||
this.stopPollTimer();
|
this.stopPollTimer();
|
||||||
|
|
||||||
if (args && args.length > 0) {
|
if (args && args.length > 0) {
|
||||||
@@ -133,10 +160,12 @@ export class DeviceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
response.success = true;
|
response.success = true;
|
||||||
|
response.modules = await this.getHardwareModules(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = {message: error.message, stack: error.stack};
|
const err = {message: error.message, stack: error.stack};
|
||||||
this.logService.error('[DeviceService] updateFirmware error', err);
|
this.logService.error('[DeviceService] updateFirmware error', err);
|
||||||
|
|
||||||
|
response.modules = await this.getHardwareModules(true);
|
||||||
response.error = err;
|
response.error = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +173,35 @@ export class DeviceService {
|
|||||||
await emptyDir(firmwarePathData.tmpDirectory.name);
|
await emptyDir(firmwarePathData.tmpDirectory.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await snooze(500);
|
||||||
|
|
||||||
|
this.pollUhkDevice();
|
||||||
|
|
||||||
|
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async recoveryDevice(event: Electron.Event): Promise<void> {
|
||||||
|
const response = new FirmwareUpgradeIpcResponse();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.stopPollTimer();
|
||||||
|
|
||||||
|
await this.operations.updateRightFirmware();
|
||||||
|
|
||||||
|
await snooze(500);
|
||||||
|
|
||||||
|
this.pollUhkDevice();
|
||||||
|
|
||||||
|
response.modules = await this.getHardwareModules(false);
|
||||||
|
response.success = true;
|
||||||
|
} catch (error) {
|
||||||
|
const err = {message: error.message, stack: error.stack};
|
||||||
|
this.logService.error('[DeviceService] updateFirmware error', err);
|
||||||
|
|
||||||
|
response.modules = await this.getHardwareModules(true);
|
||||||
|
response.error = err;
|
||||||
|
}
|
||||||
|
|
||||||
await snooze(500);
|
await snooze(500);
|
||||||
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
||||||
}
|
}
|
||||||
@@ -161,16 +219,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,16 +41,20 @@ export class HardwareConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fromBinary(buffer: UhkBuffer): HardwareConfiguration {
|
fromBinary(buffer: UhkBuffer): HardwareConfiguration {
|
||||||
this.signature = buffer.readString();
|
try {
|
||||||
this.majorVersion = buffer.readUInt8();
|
this.signature = buffer.readString();
|
||||||
this.minorVersion = buffer.readUInt8();
|
this.majorVersion = buffer.readUInt8();
|
||||||
this.patchVersion = buffer.readUInt8();
|
this.minorVersion = buffer.readUInt8();
|
||||||
this.brandId = buffer.readUInt8();
|
this.patchVersion = buffer.readUInt8();
|
||||||
this.deviceId = buffer.readUInt8();
|
this.brandId = buffer.readUInt8();
|
||||||
this.uniqueId = buffer.readUInt32();
|
this.deviceId = buffer.readUInt8();
|
||||||
this.isVendorModeOn = buffer.readBoolean();
|
this.uniqueId = buffer.readUInt32();
|
||||||
this.isIso = buffer.readBoolean();
|
this.isVendorModeOn = buffer.readBoolean();
|
||||||
return this;
|
this.isIso = buffer.readBoolean();
|
||||||
|
return this;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Please power cycle your keyboard (Invalid hardware configuration: Index out of bounds)');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toJsonObject(): any {
|
toJsonObject(): any {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
|
import { HardwareModules } from './hardware-modules';
|
||||||
|
|
||||||
export class IpcResponse {
|
export class IpcResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
error?: { message: string };
|
error?: { message: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FirmwareUpgradeIpcResponse extends IpcResponse {
|
||||||
|
modules?: HardwareModules;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export namespace Constants {
|
export namespace Constants {
|
||||||
export const AGENT_GITHUB_URL = 'https://github.com/UltimateHackingKeyboard/agent';
|
export const AGENT_GITHUB_URL = 'https://github.com/UltimateHackingKeyboard/agent';
|
||||||
|
export const FIRMWARE_GITHUB_ISSUE_URL = 'https://github.com/UltimateHackingKeyboard/agent/issues/567';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ export const getHardwareConfigFromDeviceResponse = (json: string): HardwareConfi
|
|||||||
const hardwareConfig = new HardwareConfiguration();
|
const hardwareConfig = new HardwareConfiguration();
|
||||||
hardwareConfig.fromBinary(UhkBuffer.fromArray(data));
|
hardwareConfig.fromBinary(UhkBuffer.fromArray(data));
|
||||||
|
|
||||||
if (hardwareConfig.uniqueId > 0) {
|
if (hardwareConfig.signature === 'FTY') {
|
||||||
return hardwareConfig;
|
throw Error('The device is in factory reset mode. Power-cycle the device to use it with Agent!');
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
if (hardwareConfig.signature !== 'UHK') {
|
||||||
|
throw Error('Please power cycle your keyboard (Invalid hardware configuration: Invalid signature)');
|
||||||
|
}
|
||||||
|
|
||||||
|
return hardwareConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getUserConfigFromDeviceResponse = (json: string): UserConfiguration => {
|
export const getUserConfigFromDeviceResponse = (json: string): UserConfiguration => {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
7
packages/uhk-usb/package-lock.json
generated
7
packages/uhk-usb/package-lock.json
generated
@@ -1,14 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "uhk-usb",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "8.0.28",
|
"version": "8.0.28",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
|
||||||
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==",
|
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
|
|||||||
@@ -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-es';
|
import { 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,
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
ModuleSlotToId,
|
ModuleSlotToId,
|
||||||
UsbCommand
|
UsbCommand
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { bufferToString, getTransferData, retry, snooze } from './util';
|
import { bufferToString, getTransferData, isUhkDevice, retry, snooze } from './util';
|
||||||
|
|
||||||
export const BOOTLOADER_TIMEOUT_MS = 5000;
|
export const BOOTLOADER_TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
@@ -50,12 +50,16 @@ export class UhkHidDevice {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.deviceConnected()) {
|
const dev = devices().find((x: Device) => isUhkDevice(x) || x.productId === Constants.BOOTLOADER_ID);
|
||||||
|
|
||||||
|
if (!dev) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._hasPermission = this.getDevice() !== null;
|
const device = new HID(dev.path);
|
||||||
this.close();
|
device.close();
|
||||||
|
|
||||||
|
this._hasPermission = true;
|
||||||
|
|
||||||
return this._hasPermission;
|
return this._hasPermission;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -69,15 +73,24 @@ 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 (isUhkDevice(dev)) {
|
||||||
|
result.connected = true;
|
||||||
|
} else if (dev.vendorId === Constants.VENDOR_ID &&
|
||||||
|
dev.productId === Constants.BOOTLOADER_ID) {
|
||||||
|
result.bootloaderActive = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return connected;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -144,6 +157,10 @@ export class UhkHidDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resetDeviceCache(): void {
|
||||||
|
this._prevDevices = {};
|
||||||
|
}
|
||||||
|
|
||||||
async reenumerate(enumerationMode: EnumerationModes): Promise<void> {
|
async reenumerate(enumerationMode: EnumerationModes): Promise<void> {
|
||||||
const reenumMode = EnumerationModes[enumerationMode].toString();
|
const reenumMode = EnumerationModes[enumerationMode].toString();
|
||||||
this.logService.debug(`[UhkHidDevice] Start reenumeration, mode: ${reenumMode}`);
|
this.logService.debug(`[UhkHidDevice] Start reenumeration, mode: ${reenumMode}`);
|
||||||
@@ -242,13 +259,7 @@ export class UhkHidDevice {
|
|||||||
this.logService.debug('[UhkHidDevice] Available devices unchanged');
|
this.logService.debug('[UhkHidDevice] Available devices unchanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
const dev = devs.find((x: Device) =>
|
const dev = devs.find(isUhkDevice);
|
||||||
x.vendorId === Constants.VENDOR_ID &&
|
|
||||||
x.productId === Constants.PRODUCT_ID &&
|
|
||||||
// hidapi can not read the interface number on Mac, so check the usage page and usage
|
|
||||||
((x.usagePage === 128 && x.usage === 129) || // Old firmware
|
|
||||||
(x.usagePage === (0xFF00 | 0x00) && x.usage === 0x01) || // New firmware
|
|
||||||
x.interface === 0));
|
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
this.logService.debug('[UhkHidDevice] UHK Device not found:');
|
this.logService.debug('[UhkHidDevice] UHK Device not found:');
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from './constants';
|
} from './constants';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
import { UhkBlhost } from './uhk-blhost';
|
import { UhkBlhost } from './uhk-blhost';
|
||||||
import { UhkHidDevice } from './uhk-hid-device';
|
import { UhkHidDevice } from './uhk-hid-device';
|
||||||
import { snooze } from './util';
|
import { snooze } from './util';
|
||||||
@@ -29,6 +30,7 @@ export class UhkOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async updateRightFirmware(firmwarePath = this.getFirmwarePath()) {
|
public async updateRightFirmware(firmwarePath = this.getFirmwarePath()) {
|
||||||
|
this.logService.debug(`[UhkOperations] Operating system: ${os.type()} ${os.release()} ${os.arch()}`);
|
||||||
this.logService.debug('[UhkOperations] Start flashing right firmware');
|
this.logService.debug('[UhkOperations] Start flashing right firmware');
|
||||||
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.bootloader.toString(16)}`];
|
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.bootloader.toString(16)}`];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { Device } from 'node-hid';
|
||||||
|
import { DeviceConnectionState, LogService } from 'uhk-common';
|
||||||
|
|
||||||
import { Constants, UsbCommand } from './constants';
|
import { Constants, UsbCommand } from './constants';
|
||||||
import { 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 +97,18 @@ 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isUhkDevice = (dev: Device): boolean => {
|
||||||
|
return dev.vendorId === Constants.VENDOR_ID &&
|
||||||
|
dev.productId === Constants.PRODUCT_ID &&
|
||||||
|
// hidapi can not read the interface number on Mac, so check the usage page and usage
|
||||||
|
((dev.usagePage === 128 && dev.usage === 129) || // Old firmware
|
||||||
|
(dev.usagePage === (0xFF00 | 0x00) && dev.usage === 0x01) || // New firmware
|
||||||
|
dev.interface === 0);
|
||||||
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<input #inputControl
|
||||||
|
cancelable
|
||||||
|
[class]="css"
|
||||||
|
type="text"
|
||||||
|
[disabled]="disabled"
|
||||||
|
[(ngModel)]="model"
|
||||||
|
(blur)="blur()"
|
||||||
|
(focus)="focus()"
|
||||||
|
(keyup.enter)="keyEnter($event)"
|
||||||
|
(keyup)="calculateTextWidth($event.target.value)">
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
forwardRef, HostListener,
|
||||||
|
Input,
|
||||||
|
Renderer2,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import * as util from '../../util';
|
||||||
|
|
||||||
|
const noop = (_: any) => {
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'auto-grow-input',
|
||||||
|
templateUrl: './auto-grow-input.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => AutoGrowInputComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AutoGrowInputComponent implements ControlValueAccessor {
|
||||||
|
@Input() maxParentWidthPercent = 1;
|
||||||
|
@Input() css: string;
|
||||||
|
|
||||||
|
@ViewChild('inputControl') inputControl: ElementRef;
|
||||||
|
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
get model(): string {
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
|
||||||
|
set model(value: string) {
|
||||||
|
if (this._model === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._model = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _model: string;
|
||||||
|
private _originalModel: string;
|
||||||
|
private _onChanged = noop;
|
||||||
|
private _onTouched = noop;
|
||||||
|
|
||||||
|
constructor(private _cdRef: ChangeDetectorRef,
|
||||||
|
private _renderer: Renderer2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this._onChanged = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
this._onTouched = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
if (this.disabled === isDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
this._cdRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize')
|
||||||
|
windowResize(): void {
|
||||||
|
this.calculateTextWidth(this._model);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(obj: any): void {
|
||||||
|
console.log('write', new Date());
|
||||||
|
if (this.model === obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._model = obj;
|
||||||
|
this._originalModel = obj;
|
||||||
|
this.calculateTextWidth(this._model);
|
||||||
|
this._cdRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
focus(): void {
|
||||||
|
this._onTouched(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
blur(): void {
|
||||||
|
if (!util.isValidName(this._model) || this._model.trim() === this._originalModel) {
|
||||||
|
this._model = this._originalModel;
|
||||||
|
this.calculateTextWidth(this._model);
|
||||||
|
this._cdRef.markForCheck();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._originalModel = this._model;
|
||||||
|
this._onChanged(this._model);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyEnter(event): void {
|
||||||
|
event.target.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateTextWidth(text: string): void {
|
||||||
|
const htmlInput = this.inputControl.nativeElement as HTMLInputElement;
|
||||||
|
const maxWidth = htmlInput.parentElement.parentElement.offsetWidth * this.maxParentWidthPercent;
|
||||||
|
const textWidth = util.getContentWidth(window.getComputedStyle(htmlInput), text);
|
||||||
|
this._renderer.setStyle(htmlInput, 'width', Math.min(maxWidth, textWidth) + 'px');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './auto-grow-input.component';
|
||||||
@@ -14,11 +14,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label class="btn btn-default btn-file">
|
<file-upload (fileChanged)="changeFile($event)"
|
||||||
Import device configuration
|
label="Import device configuration">
|
||||||
<input type="file"
|
</file-upload>
|
||||||
(change)="changeFile($event)">
|
|
||||||
</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button class="btn btn-danger"
|
<button class="btn btn-danger"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
SaveUserConfigInBinaryFileAction,
|
SaveUserConfigInBinaryFileAction,
|
||||||
SaveUserConfigInJsonFileAction
|
SaveUserConfigInJsonFileAction
|
||||||
} from '../../../store/actions/user-config';
|
} from '../../../store/actions/user-config';
|
||||||
|
import { UploadFileData } from '../../../models/upload-file-data';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'device-settings',
|
selector: 'device-settings',
|
||||||
@@ -42,16 +43,7 @@ export class DeviceConfigurationComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeFile(event): void {
|
changeFile(data: UploadFileData): void {
|
||||||
const files = event.srcElement.files;
|
this.store.dispatch(new LoadUserConfigurationFromFileAction(data));
|
||||||
const fileReader = new FileReader();
|
|
||||||
fileReader.onloadend = function () {
|
|
||||||
const arrayBuffer = new Uint8Array(fileReader.result);
|
|
||||||
this.store.dispatch(new LoadUserConfigurationFromFileAction({
|
|
||||||
filename: event.srcElement.value,
|
|
||||||
data: Array.from(arrayBuffer)
|
|
||||||
}));
|
|
||||||
}.bind(this);
|
|
||||||
fileReader.readAsArrayBuffer(files[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,14 +12,17 @@
|
|||||||
Firmware {{ hardwareModules.rightModuleInfo.firmwareVersion }} is running on the right keyboard half.
|
Firmware {{ hardwareModules.rightModuleInfo.firmwareVersion }} is running on the right keyboard half.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
If the update process fails, consider the following points:
|
||||||
<i>
|
<ol>
|
||||||
Please note that the firmware update process may sometimes fail. If if fails then
|
<li>Windows 7, Windows Vista, and Windows XP are not supported. Use Linux, OSX, Windows 10, or Windows 8.</li>
|
||||||
simply retry until it succeeds. If the left half becomes unresponsive after a failed
|
<li>Connect your UHK directly to the host computer. Don't use USB hubs or KVM switches.</li>
|
||||||
update then retry and follow the instructions displayed during the update to fix it.
|
<li>Run Agent directly on the host operating system. Don't use VirtualBox or VMware Workstation.</li>
|
||||||
We'll make the firmware update process more robust.
|
<li>Give a try to every USB port of your computer.</li>
|
||||||
</i>
|
<li>Remove every other USB device from your computer.</li>
|
||||||
</p>
|
<li>If the left half becomes unresponsive after a failed update then retry and follow the instructions displayed during the update to fix it.</li>
|
||||||
|
<li>If the above fails, retry a couple of times.</li>
|
||||||
|
<li>If everything else fails, please add a new comment to <a class="link-github" (click)="openFirmwareGitHubIssuePage($event)">the GitHub issue</a>, and attach the update log.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button class="btn btn-primary"
|
<button class="btn btn-primary"
|
||||||
@@ -27,27 +30,17 @@
|
|||||||
(click)="onUpdateFirmware()">
|
(click)="onUpdateFirmware()">
|
||||||
Flash firmware {{ (getAgentVersionInfo$ | async).firmwareVersion }} (bundled with Agent)
|
Flash firmware {{ (getAgentVersionInfo$ | async).firmwareVersion }} (bundled with Agent)
|
||||||
</button>
|
</button>
|
||||||
<label class="btn btn-primary btn-file"
|
<file-upload [disabled]="flashFirmwareButtonDisbabled$ | async"
|
||||||
[class.disabled]="flashFirmwareButtonDisbabled$ | async">
|
(fileChanged)="changeFile($event)"
|
||||||
Choose firmware file and flash it
|
accept=".tar.bz2"
|
||||||
<input id="firmware-file-select"
|
label="Choose firmware file and flash it"></file-upload>
|
||||||
type="file"
|
|
||||||
accept=".tar.bz2"
|
|
||||||
[disabled]="flashFirmwareButtonDisbabled$ | async"
|
|
||||||
(change)="changeFile($event)">
|
|
||||||
</label>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-grow" #scrollMe>
|
<div class="flex-grow">
|
||||||
<xterm [logs]="xtermLog$ | async"></xterm>
|
<xterm [logs]="xtermLog$ | async"></xterm>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="flex-footer">
|
||||||
<button type="button"
|
|
||||||
class="btn btn-primary ok-button"
|
|
||||||
[disabled]="firmwareOkButtonDisabled$ | async"
|
|
||||||
(click)="onOkButtonClick()">OK
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,24 +6,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-container {
|
.link-github {
|
||||||
height: 100%;
|
cursor: pointer;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
import { HardwareModules, VersionInformation } from 'uhk-common';
|
import { HardwareModules, VersionInformation } from 'uhk-common';
|
||||||
|
import { Constants } from 'uhk-common';
|
||||||
|
import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AppState,
|
AppState,
|
||||||
firmwareOkButtonDisabled,
|
|
||||||
flashFirmwareButtonDisbabled,
|
flashFirmwareButtonDisbabled,
|
||||||
getAgentVersionInfo,
|
getAgentVersionInfo,
|
||||||
getHardwareModules,
|
getHardwareModules,
|
||||||
xtermLog
|
xtermLog
|
||||||
} from '../../../store';
|
} from '../../../store';
|
||||||
import { UpdateFirmwareAction, UpdateFirmwareOkButtonAction, UpdateFirmwareWithAction } from '../../../store/actions/device';
|
import { UpdateFirmwareAction, UpdateFirmwareWithAction } from '../../../store/actions/device';
|
||||||
import { XtermLog } from '../../../models/xterm-log';
|
import { XtermLog } from '../../../models/xterm-log';
|
||||||
|
import { UploadFileData } from '../../../models/upload-file-data';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'device-firmware',
|
selector: 'device-firmware',
|
||||||
@@ -26,33 +28,20 @@ import { XtermLog } from '../../../models/xterm-log';
|
|||||||
export class DeviceFirmwareComponent implements OnDestroy {
|
export class DeviceFirmwareComponent implements OnDestroy {
|
||||||
flashFirmwareButtonDisbabled$: Observable<boolean>;
|
flashFirmwareButtonDisbabled$: Observable<boolean>;
|
||||||
xtermLog$: Observable<Array<XtermLog>>;
|
xtermLog$: Observable<Array<XtermLog>>;
|
||||||
xtermLogSubscription: Subscription;
|
|
||||||
getAgentVersionInfo$: Observable<VersionInformation>;
|
getAgentVersionInfo$: Observable<VersionInformation>;
|
||||||
firmwareOkButtonDisabled$: Observable<boolean>;
|
|
||||||
hardwareModulesSubscription: Subscription;
|
hardwareModulesSubscription: Subscription;
|
||||||
hardwareModules: HardwareModules;
|
hardwareModules: HardwareModules;
|
||||||
|
|
||||||
@ViewChild('scrollMe') divElement: ElementRef;
|
|
||||||
|
|
||||||
constructor(private store: Store<AppState>) {
|
constructor(private store: Store<AppState>) {
|
||||||
this.flashFirmwareButtonDisbabled$ = store.select(flashFirmwareButtonDisbabled);
|
this.flashFirmwareButtonDisbabled$ = store.select(flashFirmwareButtonDisbabled);
|
||||||
this.xtermLog$ = store.select(xtermLog);
|
this.xtermLog$ = store.select(xtermLog);
|
||||||
this.xtermLogSubscription = this.xtermLog$.subscribe(() => {
|
|
||||||
if (this.divElement && this.divElement.nativeElement) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.divElement.nativeElement.scrollTop = this.divElement.nativeElement.scrollHeight;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.getAgentVersionInfo$ = store.select(getAgentVersionInfo);
|
this.getAgentVersionInfo$ = store.select(getAgentVersionInfo);
|
||||||
this.firmwareOkButtonDisabled$ = store.select(firmwareOkButtonDisabled);
|
|
||||||
this.hardwareModulesSubscription = store.select(getHardwareModules).subscribe(data => {
|
this.hardwareModulesSubscription = store.select(getHardwareModules).subscribe(data => {
|
||||||
this.hardwareModules = data;
|
this.hardwareModules = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.xtermLogSubscription.unsubscribe();
|
|
||||||
this.hardwareModulesSubscription.unsubscribe();
|
this.hardwareModulesSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,22 +49,12 @@ export class DeviceFirmwareComponent implements OnDestroy {
|
|||||||
this.store.dispatch(new UpdateFirmwareAction());
|
this.store.dispatch(new UpdateFirmwareAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
onOkButtonClick(): void {
|
changeFile(data: UploadFileData): void {
|
||||||
this.store.dispatch(new UpdateFirmwareOkButtonAction());
|
this.store.dispatch(new UpdateFirmwareWithAction(data.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
changeFile(event): void {
|
openFirmwareGitHubIssuePage(event): void {
|
||||||
const files = event.srcElement.files;
|
event.preventDefault();
|
||||||
|
this.store.dispatch(new OpenUrlInNewWindowAction(Constants.FIRMWARE_GITHUB_ISSUE_URL));
|
||||||
if (files.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileReader = new FileReader();
|
|
||||||
fileReader.onloadend = function () {
|
|
||||||
const arrayBuffer = new Uint8Array(fileReader.result);
|
|
||||||
this.store.dispatch(new UpdateFirmwareWithAction(Array.prototype.slice.call(arrayBuffer)));
|
|
||||||
}.bind(this);
|
|
||||||
fileReader.readAsArrayBuffer(files[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
<xterm [logs]="xtermLog$ | async"></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,34 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
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 {
|
||||||
|
flashFirmwareButtonDisbabled$: Observable<boolean>;
|
||||||
|
|
||||||
|
xtermLog$: Observable<Array<XtermLog>>;
|
||||||
|
|
||||||
|
constructor(private store: Store<AppState>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.flashFirmwareButtonDisbabled$ = this.store.select(flashFirmwareButtonDisbabled);
|
||||||
|
this.xtermLog$ = this.store.select(xtermLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecoveryDevice(): void {
|
||||||
|
this.store.dispatch(new RecoveryDeviceAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<label class="btn btn-primary btn-file"
|
||||||
|
[class.disabled]="disabled">
|
||||||
|
{{ label }}
|
||||||
|
<input #inputControl
|
||||||
|
type="file"
|
||||||
|
[accept]="accept"
|
||||||
|
[disabled]="disabled"
|
||||||
|
(change)="changeFile($event)">
|
||||||
|
</label>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
|
||||||
|
import { UploadFileData } from '../../models/upload-file-data';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'file-upload',
|
||||||
|
templateUrl: './file-upload.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FileUploadComponent {
|
||||||
|
@Input() label = 'Select file';
|
||||||
|
@Input() disabled: boolean;
|
||||||
|
@Input() accept: string;
|
||||||
|
|
||||||
|
@Output() fileChanged = new EventEmitter<UploadFileData>();
|
||||||
|
|
||||||
|
changeFile(event): void {
|
||||||
|
const files = event.srcElement.files;
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
fileReader.onloadend = function () {
|
||||||
|
const arrayBuffer = new Uint8Array(fileReader.result);
|
||||||
|
const target = event.target || event.srcElement || event.currentTarget;
|
||||||
|
target.value = null;
|
||||||
|
this.fileChanged.emit({
|
||||||
|
filename: event.srcElement.value,
|
||||||
|
data: Array.from(arrayBuffer)
|
||||||
|
});
|
||||||
|
}.bind(this);
|
||||||
|
fileReader.readAsArrayBuffer(files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
packages/uhk-web/src/app/components/file-upload/index.ts
Normal file
1
packages/uhk-web/src/app/components/file-upload/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './file-upload.component';
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
<div>
|
<div>
|
||||||
<h4>Type text</h4>
|
<h4>Type text</h4>
|
||||||
<textarea #macroTextInput name="macro-text" (change)="onTextChange()"
|
<textarea #macroTextInput
|
||||||
(keyup)="validate()" class="macro__text-input">{{ macroAction?.text }}</textarea>
|
name="macro-text"
|
||||||
|
(keydown)="onKeydown($event)"
|
||||||
|
(change)="onTextChange()"
|
||||||
|
(keyup)="validate()"
|
||||||
|
(paste)="onPaste($event)"
|
||||||
|
class="macro__text-input">{{ macroAction?.text }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { TextMacroAction } from 'uhk-common';
|
|||||||
|
|
||||||
import { MacroBaseComponent } from '../macro-base.component';
|
import { MacroBaseComponent } from '../macro-base.component';
|
||||||
|
|
||||||
|
const NON_ASCII_REGEXP = /[^\x00-\x7F]/g;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'macro-text-tab',
|
selector: 'macro-text-tab',
|
||||||
templateUrl: './macro-text.component.html',
|
templateUrl: './macro-text.component.html',
|
||||||
@@ -36,6 +38,41 @@ export class MacroTextTabComponent extends MacroBaseComponent implements OnInit,
|
|||||||
this.macroAction.text = this.input.nativeElement.value;
|
this.macroAction.text = this.input.nativeElement.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not allow non ascii character
|
||||||
|
* @param $event
|
||||||
|
*/
|
||||||
|
onKeydown($event: KeyboardEvent): void {
|
||||||
|
if (new RegExp(NON_ASCII_REGEXP).test($event.key)) {
|
||||||
|
$event.preventDefault();
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove non ascii character from clipboard data
|
||||||
|
* @param $event
|
||||||
|
*/
|
||||||
|
onPaste($event: ClipboardEvent): void {
|
||||||
|
$event.preventDefault();
|
||||||
|
|
||||||
|
const textarea: HTMLTextAreaElement = this.input.nativeElement;
|
||||||
|
const data = $event.clipboardData.getData('text/plain');
|
||||||
|
const text = data && data.replace(NON_ASCII_REGEXP, '') || '';
|
||||||
|
if (text.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = textarea.value || '';
|
||||||
|
const prefix = value.substr(0, textarea.selectionStart);
|
||||||
|
const end = textarea.selectionEnd;
|
||||||
|
const suffix = value.substr(textarea.selectionEnd);
|
||||||
|
textarea.value = prefix + text + suffix;
|
||||||
|
const correction = end === 0 ? 0 : 1;
|
||||||
|
textarea.selectionStart = textarea.selectionEnd = end + text.length - correction;
|
||||||
|
this.macroAction.text = textarea.value;
|
||||||
|
}
|
||||||
|
|
||||||
isMacroValid = () => !!this.input.nativeElement.value;
|
isMacroValid = () => !!this.input.nativeElement.value;
|
||||||
|
|
||||||
private init = () => {
|
private init = () => {
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
<li class="sidebar__level-0--item">
|
<li class="sidebar__level-0--item">
|
||||||
<div class="sidebar__level-0">
|
<div class="sidebar__level-0">
|
||||||
<i class="uhk-icon uhk-icon-0401-usb-stick rotate-right"></i>
|
<i class="uhk-icon uhk-icon-0401-usb-stick rotate-right"></i>
|
||||||
<input #deviceName cancelable
|
<auto-grow-input [ngModel]="state.deviceName"
|
||||||
class="pane-title__name"
|
[maxParentWidthPercent]="0.65"
|
||||||
type="text"
|
[css]="'side-menu-pane-title__name'"
|
||||||
[readonly]="state.restoreUserConfiguration"
|
[disabled]="state.restoreUserConfiguration || state.updatingFirmware"
|
||||||
(change)="editDeviceName($event.target.value)"
|
(ngModelChange)="editDeviceName($event)"></auto-grow-input>
|
||||||
(keyup.enter)="deviceName.blur()"
|
|
||||||
(keyup)="calculateHeaderTextWidth($event.target.value)">
|
|
||||||
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'device')"></i>
|
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'device')"></i>
|
||||||
</div>
|
</div>
|
||||||
<ul [@toggler]="animation['device']">
|
<ul [@toggler]="animation['device']">
|
||||||
|
|||||||
@@ -162,22 +162,3 @@ ul {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pane-title {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
border: none;
|
|
||||||
border-bottom: 2px dotted #999;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 0.25rem;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
background-color: transparent;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
|
|
||||||
border-color: transparent;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
AfterContentInit,
|
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
@@ -19,7 +18,6 @@ import 'rxjs/add/operator/let';
|
|||||||
|
|
||||||
import { AppState, getSideMenuPageState } from '../../store';
|
import { AppState, getSideMenuPageState } from '../../store';
|
||||||
import { MacroActions } from '../../store/actions';
|
import { MacroActions } from '../../store/actions';
|
||||||
import * as util from '../../util';
|
|
||||||
import { RenameUserConfigurationAction } from '../../store/actions/user-config';
|
import { RenameUserConfigurationAction } from '../../store/actions/user-config';
|
||||||
import { SideMenuPageState } from '../../models/side-menu-page-state';
|
import { SideMenuPageState } from '../../models/side-menu-page-state';
|
||||||
|
|
||||||
@@ -40,7 +38,7 @@ import { SideMenuPageState } from '../../models/side-menu-page-state';
|
|||||||
styleUrls: ['./side-menu.component.scss'],
|
styleUrls: ['./side-menu.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy {
|
export class SideMenuComponent implements OnInit, OnDestroy {
|
||||||
state: SideMenuPageState;
|
state: SideMenuPageState;
|
||||||
animation: { [key: string]: 'active' | 'inactive' };
|
animation: { [key: string]: 'active' | 'inactive' };
|
||||||
@ViewChild('deviceName') deviceName: ElementRef;
|
@ViewChild('deviceName') deviceName: ElementRef;
|
||||||
@@ -62,15 +60,10 @@ export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.stateSubscription = this.store.select(getSideMenuPageState).subscribe(data => {
|
this.stateSubscription = this.store.select(getSideMenuPageState).subscribe(data => {
|
||||||
this.state = data;
|
this.state = data;
|
||||||
this.setDeviceName();
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit(): void {
|
|
||||||
this.setDeviceName();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.stateSubscription) {
|
if (this.stateSubscription) {
|
||||||
this.stateSubscription.unsubscribe();
|
this.stateSubscription.unsubscribe();
|
||||||
@@ -106,24 +99,6 @@ export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
editDeviceName(name: string): void {
|
editDeviceName(name: string): void {
|
||||||
if (!util.isValidName(name) || name.trim() === this.state.deviceName) {
|
|
||||||
this.setDeviceName();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.store.dispatch(new RenameUserConfigurationAction(name));
|
this.store.dispatch(new RenameUserConfigurationAction(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateHeaderTextWidth(text): void {
|
|
||||||
const htmlInput = this.deviceName.nativeElement as HTMLInputElement;
|
|
||||||
const maxWidth = htmlInput.parentElement.offsetWidth * 0.66;
|
|
||||||
const textWidth = util.getContentWidth(window.getComputedStyle(htmlInput), text);
|
|
||||||
this.renderer.setStyle(htmlInput, 'width', Math.min(maxWidth, textWidth) + 'px');
|
|
||||||
}
|
|
||||||
|
|
||||||
private setDeviceName(): void {
|
|
||||||
if (this.deviceName) {
|
|
||||||
this.renderer.setProperty(this.deviceName.nativeElement, 'value', this.state.deviceName);
|
|
||||||
this.calculateHeaderTextWidth(this.deviceName.nativeElement.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
<div class="wrapper">
|
<div class="x-term-container">
|
||||||
<ul class="list-unstyled">
|
<div class="x-term-wrapper" #scrollMe>
|
||||||
<li *ngFor="let log of logs" [ngClass]="log.cssClass"><span>{{ log.message }}</span></li>
|
<ul class="list-unstyled">
|
||||||
</ul>
|
<li *ngFor="let log of logs" [ngClass]="log.cssClass"><span>{{ log.message }}</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="copy-container-wrapper">
|
||||||
|
<div class="copy-container">
|
||||||
|
<span class="fa fa-2x fa-copy"
|
||||||
|
ngxClipboard
|
||||||
|
[cbContent]="getClipboardContent()"
|
||||||
|
title="Copy to clipboard"
|
||||||
|
data-toggle="tooltip"
|
||||||
|
data-placement="top"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,36 @@
|
|||||||
|
$scrollbar-color: #ffffff;
|
||||||
|
$scrollbar-radius: 6px;
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
background-color: yellow;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.x-term-container {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-term-wrapper {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
|
overflow: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-container-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
right: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.xterm-standard {
|
.xterm-standard {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
|
||||||
import { XtermLog } from '../../models/xterm-log';
|
import { XtermLog } from '../../models/xterm-log';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -7,6 +7,21 @@ import { XtermLog } from '../../models/xterm-log';
|
|||||||
styleUrls: ['./xterm.component.scss'],
|
styleUrls: ['./xterm.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class XtermComponent {
|
export class XtermComponent implements OnChanges {
|
||||||
@Input() logs: Array<XtermLog> = [];
|
@Input() logs: Array<XtermLog> = [];
|
||||||
|
|
||||||
|
@ViewChild('scrollMe') divElement: ElementRef;
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes.logs && this.divElement && this.divElement.nativeElement) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.divElement.nativeElement.scrollTop = this.divElement.nativeElement.scrollHeight;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getClipboardContent(): string {
|
||||||
|
return this.logs.reduce((value, line) => value + line.message + '\n', '');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { HardwareModules } from 'uhk-common';
|
||||||
|
|
||||||
|
export interface FirmwareUpgradeError {
|
||||||
|
error: any;
|
||||||
|
modules?: HardwareModules;
|
||||||
|
}
|
||||||
@@ -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,9 @@ 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';
|
||||||
|
import { FileUploadComponent } from './components/file-upload';
|
||||||
|
import { AutoGrowInputComponent } from './components/auto-grow-input';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -176,7 +180,10 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
|
|||||||
SliderWrapperComponent,
|
SliderWrapperComponent,
|
||||||
EditableTextComponent,
|
EditableTextComponent,
|
||||||
Autofocus,
|
Autofocus,
|
||||||
RestoreConfigurationComponent
|
RestoreConfigurationComponent,
|
||||||
|
RecoveryModeComponent,
|
||||||
|
FileUploadComponent,
|
||||||
|
AutoGrowInputComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -211,7 +218,8 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
|
|||||||
UhkDeviceInitializedGuard,
|
UhkDeviceInitializedGuard,
|
||||||
UhkDeviceUninitializedGuard,
|
UhkDeviceUninitializedGuard,
|
||||||
UhkDeviceLoadingGuard,
|
UhkDeviceLoadingGuard,
|
||||||
UhkDeviceLoadedGuard
|
UhkDeviceLoadedGuard,
|
||||||
|
UhkDeviceBootloaderNotActiveGuard
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
UhkMessageComponent,
|
UhkMessageComponent,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
import { DeviceConnectionState, HardwareModules, IpcResponse, type } from 'uhk-common';
|
import { DeviceConnectionState, FirmwareUpgradeIpcResponse, HardwareModules, IpcResponse, type } from 'uhk-common';
|
||||||
|
import { FirmwareUpgradeError } from '../../models/firmware-upgrade-error';
|
||||||
|
|
||||||
const PREFIX = '[device] ';
|
const PREFIX = '[device] ';
|
||||||
|
|
||||||
@@ -26,7 +27,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 {
|
||||||
@@ -95,25 +97,23 @@ export class UpdateFirmwareWithAction implements Action {
|
|||||||
export class UpdateFirmwareReplyAction implements Action {
|
export class UpdateFirmwareReplyAction implements Action {
|
||||||
type = ActionTypes.UPDATE_FIRMWARE_REPLY;
|
type = ActionTypes.UPDATE_FIRMWARE_REPLY;
|
||||||
|
|
||||||
constructor(public payload: IpcResponse) {
|
constructor(public payload: FirmwareUpgradeIpcResponse) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateFirmwareSuccessAction implements Action {
|
export class UpdateFirmwareSuccessAction implements Action {
|
||||||
type = ActionTypes.UPDATE_FIRMWARE_SUCCESS;
|
type = ActionTypes.UPDATE_FIRMWARE_SUCCESS;
|
||||||
|
constructor(public payload: HardwareModules) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateFirmwareFailedAction implements Action {
|
export class UpdateFirmwareFailedAction implements Action {
|
||||||
type = ActionTypes.UPDATE_FIRMWARE_FAILED;
|
type = ActionTypes.UPDATE_FIRMWARE_FAILED;
|
||||||
|
|
||||||
constructor(public payload: any) {
|
constructor(public payload: FirmwareUpgradeError) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateFirmwareOkButtonAction implements Action {
|
|
||||||
type = ActionTypes.UPDATE_FIRMWARE_OK_BUTTON;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ResetMouseSpeedSettingsAction implements Action {
|
export class ResetMouseSpeedSettingsAction implements Action {
|
||||||
type = ActionTypes.RESET_MOUSE_SPEED_SETTINGS;
|
type = ActionTypes.RESET_MOUSE_SPEED_SETTINGS;
|
||||||
}
|
}
|
||||||
@@ -140,6 +140,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
|
||||||
@@ -157,9 +161,9 @@ export type Actions
|
|||||||
| UpdateFirmwareReplyAction
|
| UpdateFirmwareReplyAction
|
||||||
| UpdateFirmwareSuccessAction
|
| UpdateFirmwareSuccessAction
|
||||||
| UpdateFirmwareFailedAction
|
| UpdateFirmwareFailedAction
|
||||||
| UpdateFirmwareOkButtonAction
|
|
||||||
| HardwareModulesLoadedAction
|
| HardwareModulesLoadedAction
|
||||||
| 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
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import 'rxjs/add/operator/withLatestFrom';
|
|||||||
import 'rxjs/add/operator/switchMap';
|
import 'rxjs/add/operator/switchMap';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DeviceConnectionState,
|
FirmwareUpgradeIpcResponse,
|
||||||
HardwareConfiguration,
|
HardwareConfiguration,
|
||||||
IpcResponse,
|
IpcResponse,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
ActionTypes,
|
ActionTypes,
|
||||||
ConnectionStateChangedAction,
|
ConnectionStateChangedAction,
|
||||||
HideSaveToKeyboardButton,
|
HideSaveToKeyboardButton,
|
||||||
|
RecoveryDeviceAction,
|
||||||
ResetUserConfigurationAction,
|
ResetUserConfigurationAction,
|
||||||
RestoreUserConfigurationFromBackupSuccessAction,
|
RestoreUserConfigurationFromBackupSuccessAction,
|
||||||
SaveConfigurationAction,
|
SaveConfigurationAction,
|
||||||
@@ -33,14 +34,13 @@ import {
|
|||||||
SetPrivilegeOnLinuxReplyAction,
|
SetPrivilegeOnLinuxReplyAction,
|
||||||
UpdateFirmwareAction,
|
UpdateFirmwareAction,
|
||||||
UpdateFirmwareFailedAction,
|
UpdateFirmwareFailedAction,
|
||||||
UpdateFirmwareOkButtonAction,
|
|
||||||
UpdateFirmwareReplyAction,
|
UpdateFirmwareReplyAction,
|
||||||
UpdateFirmwareSuccessAction,
|
UpdateFirmwareSuccessAction,
|
||||||
UpdateFirmwareWithAction
|
UpdateFirmwareWithAction
|
||||||
} from '../actions/device';
|
} from '../actions/device';
|
||||||
import { DeviceRendererService } from '../../services/device-renderer.service';
|
import { DeviceRendererService } from '../../services/device-renderer.service';
|
||||||
import { SetupPermissionErrorAction, ShowNotificationAction } from '../actions/app';
|
import { SetupPermissionErrorAction, ShowNotificationAction } from '../actions/app';
|
||||||
import { AppState } from '../index';
|
import { AppState, getRouterState } from '../index';
|
||||||
import {
|
import {
|
||||||
ActionTypes as UserConfigActions,
|
ActionTypes as UserConfigActions,
|
||||||
ApplyUserConfigurationFromFileAction,
|
ApplyUserConfigurationFromFileAction,
|
||||||
@@ -55,11 +55,20 @@ export class DeviceEffects {
|
|||||||
@Effect()
|
@Effect()
|
||||||
deviceConnectionStateChange$: Observable<Action> = this.actions$
|
deviceConnectionStateChange$: Observable<Action> = this.actions$
|
||||||
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
|
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
|
||||||
.map(action => action.payload)
|
.withLatestFrom(this.store.select(getRouterState))
|
||||||
.do((state: DeviceConnectionState) => {
|
.do(([action, route]) => {
|
||||||
|
const state = action.payload;
|
||||||
|
|
||||||
|
if (route.state && route.state.url.startsWith('/device/firmware')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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(['/']);
|
||||||
}
|
}
|
||||||
@@ -67,7 +76,9 @@ export class DeviceEffects {
|
|||||||
this.router.navigate(['/detection']);
|
this.router.navigate(['/detection']);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.switchMap((state: DeviceConnectionState) => {
|
.switchMap(([action, route]) => {
|
||||||
|
const state = action.payload;
|
||||||
|
|
||||||
if (state.connected && state.hasPermission) {
|
if (state.connected && state.hasPermission) {
|
||||||
return Observable.of(new LoadConfigFromDeviceAction());
|
return Observable.of(new LoadConfigFromDeviceAction());
|
||||||
}
|
}
|
||||||
@@ -90,7 +101,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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,22 +210,26 @@ export class DeviceEffects {
|
|||||||
@Effect() updateFirmwareReply$ = this.actions$
|
@Effect() updateFirmwareReply$ = this.actions$
|
||||||
.ofType<UpdateFirmwareReplyAction>(ActionTypes.UPDATE_FIRMWARE_REPLY)
|
.ofType<UpdateFirmwareReplyAction>(ActionTypes.UPDATE_FIRMWARE_REPLY)
|
||||||
.map(action => action.payload)
|
.map(action => action.payload)
|
||||||
.switchMap((response: IpcResponse) => {
|
.switchMap((response: FirmwareUpgradeIpcResponse)
|
||||||
|
: Observable<UpdateFirmwareSuccessAction | UpdateFirmwareFailedAction> => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
return Observable.of(new UpdateFirmwareSuccessAction());
|
return Observable.of(new UpdateFirmwareSuccessAction(response.modules));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Observable.of(new UpdateFirmwareFailedAction(response.error));
|
return Observable.of(new UpdateFirmwareFailedAction({
|
||||||
|
error: response.error,
|
||||||
|
modules: response.modules
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@Effect({dispatch: false}) updateFirmwareOkButton$ = this.actions$
|
|
||||||
.ofType<UpdateFirmwareOkButtonAction>(ActionTypes.UPDATE_FIRMWARE_OK_BUTTON)
|
|
||||||
.do(() => this.deviceRendererService.startConnectionPoller());
|
|
||||||
|
|
||||||
@Effect() restoreUserConfiguration$ = this.actions$
|
@Effect() restoreUserConfiguration$ = this.actions$
|
||||||
.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,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
|
|
||||||
import { DataStorageRepositoryService } from '../../services/datastorage-repository.service';
|
import { DataStorageRepositoryService } from '../../services/datastorage-repository.service';
|
||||||
import { DefaultUserConfigurationService } from '../../services/default-user-configuration.service';
|
import { DefaultUserConfigurationService } from '../../services/default-user-configuration.service';
|
||||||
import { AppState, getPrevUserConfiguration, getUserConfiguration } from '../index';
|
import { AppState, getPrevUserConfiguration, getRouterState, getUserConfiguration } from '../index';
|
||||||
import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions';
|
import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions';
|
||||||
import {
|
import {
|
||||||
DismissUndoNotificationAction,
|
DismissUndoNotificationAction,
|
||||||
@@ -118,8 +118,10 @@ export class UserConfigEffects {
|
|||||||
|
|
||||||
@Effect() loadConfigFromDeviceReply$ = this.actions$
|
@Effect() loadConfigFromDeviceReply$ = this.actions$
|
||||||
.ofType<LoadConfigFromDeviceReplyAction>(ActionTypes.LOAD_CONFIG_FROM_DEVICE_REPLY)
|
.ofType<LoadConfigFromDeviceReplyAction>(ActionTypes.LOAD_CONFIG_FROM_DEVICE_REPLY)
|
||||||
.map(action => action.payload)
|
.withLatestFrom(this.store.select(getRouterState))
|
||||||
.mergeMap((data: ConfigurationReply): any => {
|
.mergeMap(([action, route]): any => {
|
||||||
|
const data: ConfigurationReply = action.payload;
|
||||||
|
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
return [new ShowNotificationAction({
|
return [new ShowNotificationAction({
|
||||||
type: NotificationType.Error,
|
type: NotificationType.Error,
|
||||||
@@ -128,12 +130,16 @@ export class UserConfigEffects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = [];
|
const result = [];
|
||||||
let newPageDestination = ['/'];
|
let newPageDestination: Array<string>;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userConfig = getUserConfigFromDeviceResponse(data.userConfiguration);
|
const userConfig = getUserConfigFromDeviceResponse(data.userConfiguration);
|
||||||
result.push(new LoadUserConfigSuccessAction(userConfig));
|
result.push(new LoadUserConfigSuccessAction(userConfig));
|
||||||
|
|
||||||
|
if (route.state && !route.state.url.startsWith('/device/firmware')) {
|
||||||
|
newPageDestination = ['/'];
|
||||||
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logService.error('Eeprom user-config parse error:', err);
|
this.logService.error('Eeprom user-config parse error:', err);
|
||||||
const userConfig = new UserConfiguration().fromJsonObject(data.backupConfiguration);
|
const userConfig = new UserConfiguration().fromJsonObject(data.backupConfiguration);
|
||||||
@@ -158,7 +164,9 @@ export class UserConfigEffects {
|
|||||||
|
|
||||||
result.push(new HardwareModulesLoadedAction(data.modules));
|
result.push(new HardwareModulesLoadedAction(data.modules));
|
||||||
|
|
||||||
this.router.navigate(newPageDestination);
|
if (newPageDestination) {
|
||||||
|
this.router.navigate(newPageDestination);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { MetaReducer } from '@ngrx/store';
|
import { ActionReducerMap, MetaReducer } from '@ngrx/store';
|
||||||
import { RouterReducerState } from '@ngrx/router-store';
|
import { RouterReducerState, routerReducer } from '@ngrx/router-store';
|
||||||
import { storeFreeze } from 'ngrx-store-freeze';
|
import { storeFreeze } from 'ngrx-store-freeze';
|
||||||
import { Keymap, UserConfiguration } from 'uhk-common';
|
import { Keymap, UserConfiguration } from 'uhk-common';
|
||||||
|
|
||||||
@@ -14,15 +14,6 @@ import { initProgressButtonState } from './reducers/progress-button-state';
|
|||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
import { RouterStateUrl } from './router-util';
|
import { RouterStateUrl } from './router-util';
|
||||||
|
|
||||||
export const reducers = {
|
|
||||||
userConfiguration: fromUserConfig.reducer,
|
|
||||||
presetKeymaps: fromPreset.reducer,
|
|
||||||
autoUpdateSettings: autoUpdateSettings.reducer,
|
|
||||||
app: fromApp.reducer,
|
|
||||||
appUpdate: fromAppUpdate.reducer,
|
|
||||||
device: fromDevice.reducer
|
|
||||||
};
|
|
||||||
|
|
||||||
// State interface for the application
|
// State interface for the application
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
userConfiguration: UserConfiguration;
|
userConfiguration: UserConfiguration;
|
||||||
@@ -34,6 +25,16 @@ export interface AppState {
|
|||||||
device: fromDevice.State;
|
device: fromDevice.State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const reducers: ActionReducerMap<AppState> = {
|
||||||
|
userConfiguration: fromUserConfig.reducer,
|
||||||
|
presetKeymaps: fromPreset.reducer,
|
||||||
|
autoUpdateSettings: autoUpdateSettings.reducer,
|
||||||
|
app: fromApp.reducer,
|
||||||
|
router: routerReducer,
|
||||||
|
appUpdate: fromAppUpdate.reducer,
|
||||||
|
device: fromDevice.reducer
|
||||||
|
};
|
||||||
|
|
||||||
export const metaReducers: MetaReducer<AppState>[] = environment.production
|
export const metaReducers: MetaReducer<AppState>[] = environment.production
|
||||||
? []
|
? []
|
||||||
: [storeFreeze];
|
: [storeFreeze];
|
||||||
@@ -73,12 +74,12 @@ export const saveToKeyboardState = createSelector(runningInElectron, saveToKeybo
|
|||||||
});
|
});
|
||||||
export const updatingFirmware = createSelector(deviceState, fromDevice.updatingFirmware);
|
export const updatingFirmware = createSelector(deviceState, fromDevice.updatingFirmware);
|
||||||
export const xtermLog = createSelector(deviceState, fromDevice.xtermLog);
|
export const xtermLog = createSelector(deviceState, fromDevice.xtermLog);
|
||||||
export const firmwareOkButtonDisabled = createSelector(deviceState, fromDevice.firmwareOkButtonDisabled);
|
|
||||||
// tslint:disable-next-line: max-line-length
|
// tslint:disable-next-line: max-line-length
|
||||||
export const flashFirmwareButtonDisbabled = createSelector(runningInElectron, deviceState, (electron, state: fromDevice.State) => !electron || state.updatingFirmware);
|
export const flashFirmwareButtonDisbabled = createSelector(runningInElectron, deviceState, (electron, state: fromDevice.State) => !electron || state.updatingFirmware);
|
||||||
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,
|
||||||
@@ -102,3 +103,5 @@ export const getSideMenuPageState = createSelector(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getRouterState = (state: AppState) => state.router;
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
HardwareModulesLoadedAction,
|
HardwareModulesLoadedAction,
|
||||||
SaveConfigurationAction,
|
SaveConfigurationAction,
|
||||||
HasBackupUserConfigurationAction,
|
HasBackupUserConfigurationAction,
|
||||||
UpdateFirmwareFailedAction
|
UpdateFirmwareFailedAction,
|
||||||
|
UpdateFirmwareSuccessAction
|
||||||
} from '../actions/device';
|
} from '../actions/device';
|
||||||
import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app';
|
import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app';
|
||||||
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
|
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
|
||||||
@@ -17,7 +18,9 @@ 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;
|
||||||
|
savingToKeyboard: boolean;
|
||||||
updatingFirmware: boolean;
|
updatingFirmware: boolean;
|
||||||
firmwareUpdateFinished: boolean;
|
firmwareUpdateFinished: boolean;
|
||||||
modules: HardwareModules;
|
modules: HardwareModules;
|
||||||
@@ -29,7 +32,9 @@ 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,
|
||||||
|
savingToKeyboard: false,
|
||||||
updatingFirmware: false,
|
updatingFirmware: false,
|
||||||
firmwareUpdateFinished: false,
|
firmwareUpdateFinished: false,
|
||||||
modules: {
|
modules: {
|
||||||
@@ -46,14 +51,15 @@ export const initialState: State = {
|
|||||||
hasBackupUserConfiguration: false
|
hasBackupUserConfiguration: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export function reducer(state = initialState, action: Action) {
|
export function reducer(state = initialState, action: Action): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.CONNECTION_STATE_CHANGED: {
|
case ActionTypes.CONNECTION_STATE_CHANGED: {
|
||||||
const data = (<ConnectionStateChangedAction>action).payload;
|
const data = (<ConnectionStateChangedAction>action).payload;
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
connected: data.connected,
|
connected: data.connected,
|
||||||
hasPermission: data.hasPermission
|
hasPermission: data.hasPermission,
|
||||||
|
bootloaderActive: data.bootloaderActive
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,12 +135,14 @@ export function reducer(state = initialState, action: Action) {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
updatingFirmware: false,
|
updatingFirmware: false,
|
||||||
firmwareUpdateFinished: true
|
firmwareUpdateFinished: true,
|
||||||
|
modules: (action as UpdateFirmwareSuccessAction).payload
|
||||||
};
|
};
|
||||||
|
|
||||||
case ActionTypes.UPDATE_FIRMWARE_FAILED: {
|
case ActionTypes.UPDATE_FIRMWARE_FAILED: {
|
||||||
|
const data = (action as UpdateFirmwareFailedAction).payload;
|
||||||
const logEntry = {
|
const logEntry = {
|
||||||
message: (action as UpdateFirmwareFailedAction).payload.message,
|
message: data.error.message,
|
||||||
cssClass: XtermCssClass.error
|
cssClass: XtermCssClass.error
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -142,6 +150,7 @@ export function reducer(state = initialState, action: Action) {
|
|||||||
...state,
|
...state,
|
||||||
updatingFirmware: false,
|
updatingFirmware: false,
|
||||||
firmwareUpdateFinished: true,
|
firmwareUpdateFinished: true,
|
||||||
|
modules: data.modules,
|
||||||
log: [...state.log, logEntry]
|
log: [...state.log, logEntry]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -193,6 +202,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;
|
||||||
}
|
}
|
||||||
@@ -203,7 +219,6 @@ export const isDeviceConnected = (state: State) => state.connected || state.upda
|
|||||||
export const hasDevicePermission = (state: State) => state.hasPermission;
|
export const hasDevicePermission = (state: State) => state.hasPermission;
|
||||||
export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
|
export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
|
||||||
export const xtermLog = (state: State) => state.log;
|
export const xtermLog = (state: State) => state.log;
|
||||||
export const firmwareOkButtonDisabled = (state: State) => !state.firmwareUpdateFinished;
|
|
||||||
export const getHardwareModules = (state: State) => state.modules;
|
export const getHardwareModules = (state: State) => state.modules;
|
||||||
export const getHasBackupUserConfiguration = (state: State) => state.hasBackupUserConfiguration;
|
export const getHasBackupUserConfiguration = (state: State) => state.hasBackupUserConfiguration;
|
||||||
export const getBackupUserConfigurationState = (state: State): RestoreConfigurationState => {
|
export const getBackupUserConfigurationState = (state: State): RestoreConfigurationState => {
|
||||||
@@ -212,3 +227,4 @@ export const getBackupUserConfigurationState = (state: State): RestoreConfigurat
|
|||||||
hasBackupUserConfiguration: state.hasBackupUserConfiguration
|
hasBackupUserConfiguration: state.hasBackupUserConfiguration
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
export const bootloaderActive = (state: State) => state.bootloaderActive;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Action } from '@ngrx/store';
|
|||||||
export interface ProgressButtonState {
|
export interface ProgressButtonState {
|
||||||
showButton: boolean;
|
showButton: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
showProgress: boolean;
|
showProgress?: boolean;
|
||||||
action?: Action;
|
action?: Action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@import '~font-awesome/scss/font-awesome';
|
@import '~font-awesome/scss/font-awesome';
|
||||||
@import './styles/tooltip';
|
@import './styles/tooltip';
|
||||||
@import './styles/uhk-icons/uhk-icon';
|
@import './styles/uhk-icons/uhk-icon';
|
||||||
|
@import './styles/side-menu';
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -155,3 +156,25 @@ pre {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-container {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-grow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-footer {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ok-button {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|||||||
22
packages/uhk-web/src/styles/_side-menu.scss
Normal file
22
packages/uhk-web/src/styles/_side-menu.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
.side-menu-pane-title {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px dotted #999;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0.25rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
packages/usb/erase-hardware-config.js
Executable file
8
packages/usb/erase-hardware-config.js
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
const uhk = require('./uhk');
|
||||||
|
|
||||||
|
const device = uhk.getUhkDevice();
|
||||||
|
uhk.eraseHca(device)
|
||||||
|
.catch((err)=>{
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
10
packages/usb/erase-user-config.js
Executable file
10
packages/usb/erase-user-config.js
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
const fs = require('fs');
|
||||||
|
const uhk = require('./uhk');
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const device = uhk.getUhkDevice();
|
||||||
|
const buffer = new Buffer(Array(2**15-64).fill(0xff));
|
||||||
|
await uhk.writeConfig(device, buffer, false);
|
||||||
|
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, uhk.configBufferIds.stagingUserConfig);
|
||||||
|
})();
|
||||||
@@ -2,9 +2,12 @@
|
|||||||
const uhk = require('./uhk');
|
const uhk = require('./uhk');
|
||||||
const program = require('commander');
|
const program = require('commander');
|
||||||
|
|
||||||
program.parse(process.argv);
|
program
|
||||||
|
.option('-t, --timeout <ms>', 'Bootloader timeout in ms', 5000)
|
||||||
|
.parse(process.argv);
|
||||||
|
|
||||||
const enumerationMode = program.args[0];
|
const enumerationMode = program.args[0];
|
||||||
|
|
||||||
(async function() {
|
(async function() {
|
||||||
await uhk.reenumerate(enumerationMode);
|
await uhk.reenumerate(enumerationMode, program.timeout);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -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 = '';
|
||||||
@@ -191,8 +189,7 @@ async function updateDeviceFirmware(firmwareImage, extension) {
|
|||||||
|
|
||||||
// USB commands
|
// USB commands
|
||||||
|
|
||||||
function reenumerate(enumerationMode) {
|
function reenumerate(enumerationMode, bootloaderTimeoutMs=5000) {
|
||||||
const bootloaderTimeoutMs = 5000;
|
|
||||||
const pollingIntervalMs = 100;
|
const pollingIntervalMs = 100;
|
||||||
let pollingTimeoutMs = 10000;
|
let pollingTimeoutMs = 10000;
|
||||||
|
|
||||||
@@ -396,6 +393,12 @@ async function writeHca(device, isIso) {
|
|||||||
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, configBufferIds.hardwareConfig);
|
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, configBufferIds.hardwareConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function eraseHca(device) {
|
||||||
|
const buffer = new Buffer(Array(64).fill(0xff));
|
||||||
|
await uhk.writeConfig(device, buffer, true);
|
||||||
|
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, configBufferIds.hardwareConfig);
|
||||||
|
}
|
||||||
|
|
||||||
async function getModuleProperty(device, slotId, moduleProperty) {
|
async function getModuleProperty(device, slotId, moduleProperty) {
|
||||||
await writeDevice(device, [uhk.usbCommands.getModuleProperty, slotId, moduleProperty]);
|
await writeDevice(device, [uhk.usbCommands.getModuleProperty, slotId, moduleProperty]);
|
||||||
}
|
}
|
||||||
@@ -426,6 +429,7 @@ uhk = exports = module.exports = moduleExports = {
|
|||||||
launchEepromTransfer,
|
launchEepromTransfer,
|
||||||
writeUca,
|
writeUca,
|
||||||
writeHca,
|
writeHca,
|
||||||
|
eraseHca,
|
||||||
getModuleProperty,
|
getModuleProperty,
|
||||||
usbCommands: {
|
usbCommands: {
|
||||||
getDeviceProperty : 0x00,
|
getDeviceProperty : 0x00,
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
const program = require('commander');
|
|
||||||
const fs = require('fs');
|
|
||||||
const uhk = require('./uhk');
|
|
||||||
|
|
||||||
(async function() {
|
|
||||||
const device = uhk.getUhkDevice();
|
|
||||||
require('shelljs/global');
|
|
||||||
|
|
||||||
program
|
|
||||||
.usage(`config.bin`)
|
|
||||||
.option('-h, --hardware-config', 'Write the hardware config instead of the user config')
|
|
||||||
.parse(process.argv);
|
|
||||||
|
|
||||||
if (program.args.length == 0) {
|
|
||||||
console.error('No binary config file specified.');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const configBin = program.args[0];
|
|
||||||
const isHardwareConfig = program.hardwareConfig;
|
|
||||||
const configTypeString = isHardwareConfig ? 'hardware' : 'user';
|
|
||||||
const configBuffer = fs.readFileSync(configBin);
|
|
||||||
|
|
||||||
await uhk.writeUserConfig(device, configBuffer, isHardwareConfig);
|
|
||||||
})();
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
const uhk = require('./uhk');
|
const uhk = require('./uhk');
|
||||||
|
|
||||||
if (process.argv.length < 2) {
|
if (process.argv.length < 2) {
|
||||||
console.log(`use: write-hca {iso|ansi}`);
|
console.log(`use: write-hardware-config {iso|ansi}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,7 +12,8 @@ if (layout !== 'iso' && layout !== 'ansi') {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uhk.writeHca(layout === 'iso')
|
const device = uhk.getUhkDevice();
|
||||||
|
uhk.writeHca(device, layout === 'iso')
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
@@ -17,5 +17,6 @@ const uhk = require('./uhk');
|
|||||||
const device = uhk.getUhkDevice();
|
const device = uhk.getUhkDevice();
|
||||||
const configBuffer = fs.readFileSync(configPath);
|
const configBuffer = fs.readFileSync(configPath);
|
||||||
await uhk.writeConfig(device, configBuffer, false);
|
await uhk.writeConfig(device, configBuffer, false);
|
||||||
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, uhk.configBufferIds.stagingUserConfig);
|
await uhk.applyConfig(device);
|
||||||
|
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, uhk.configBufferIds.validatedUserConfig);
|
||||||
})();
|
})();
|
||||||
Reference in New Issue
Block a user