2 Commits

Author SHA1 Message Date
Róbert Kiss
f3e21ee9e4 Merge branch 'master' into chore-rebuild-uhk-web-package-lock 2019-08-23 23:19:15 +02:00
Róbert Kiss
61ddb49b0f chore: rebuild the uhk-web package-lock.json to refresh deps of deps 2019-08-23 23:17:51 +02:00
31 changed files with 3197 additions and 2945 deletions

View File

@@ -13,9 +13,7 @@ Firmware: 8.5.**4** [[release](https://github.com/UltimateHackingKeyboard/firmwa
- Implement the Kinetis bootloader protocol natively instead of relying on blhost.
- Fix device recovery mode.
- Correctly display whether the UHK is detected.
- Animate keyboard splitting, merging, and the presence of the left half.
- Don't disable input in the key action popover after adding a layer switch action, deleting it, and trying to edit it on its layer.
- Provide reasonable default mouse settings for Macs.
- Don't change tab immediately upon closing the key action popover.
- Fix UI glitch that occurrs when hitting Tab after updating keymap description.
- Make the Agent icon slightly smaller to be consistent with most application icons.

View File

@@ -12,7 +12,7 @@ Agent is the configuration application of the [Ultimate Hacking Keyboard](https:
### Step 1: Build Dependencies
You'll need Node.js 12. Use your OS package manager to install it. [Check the NodeJS site for more info.](https://nodejs.org/en/download/package-manager/ "Installing Node.js via package manager") Mac OS users can simply `brew install node` to get both. Should you need multiple Node.js versions on the same computer, use Node Version Manager for [Mac/Linux](https://github.com/creationix/nvm) or for [Windows](https://github.com/coreybutler/nvm-windows)
You'll need Node.js LTS. Use your OS package manager to install it. [Check the NodeJS site for more info.](https://nodejs.org/en/download/package-manager/ "Installing Node.js via package manager") Mac OS users can simply `brew install node` to get both. Should you need multiple Node.js versions on the same computer, use Node Version Manager for [Mac/Linux](https://github.com/creationix/nvm) or for [Windows](https://github.com/coreybutler/nvm-windows)
You'll also need `libusb`.
On debian-based linux distros, `apt-get install libusb-dev libudev-dev g++` is sufficient.

3616
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,9 @@
"author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js",
"version": "1.2.13",
"firmwareVersion": "8.6.0",
"deviceProtocolVersion": "4.5.0",
"userConfigVersion": "4.1.1",
"firmwareVersion": "8.5.4",
"deviceProtocolVersion": "4.4.0",
"userConfigVersion": "4.0.1",
"hardwareConfigVersion": "1.0.0",
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
"repository": {
@@ -27,15 +27,16 @@
"@types/jasmine": "3.3.12",
"@types/jasminewd2": "2.0.3",
"@types/jquery": "3.3.29",
"@types/jsonfile": "5.0.0",
"@types/jsonfile": "4.0.1",
"@types/lodash": "4.14.136",
"@types/node": "8.0.53",
"@types/node-hid": "0.7.2",
"@types/node-hid": "0.7.0",
"@types/request": "2.0.8",
"@types/semver": "5.5.0",
"@types/tmp": "0.0.33",
"autoprefixer": "6.5.3",
"buffer": "5.0.6",
"check-node-version": "4.0.1",
"check-node-version": "^3.2.0",
"copy-webpack-plugin": "5.0.0",
"copyfiles": "2.1.1",
"core-js": "2.4.1",
@@ -44,25 +45,29 @@
"decompress-tarbz2": "4.1.1",
"devtron": "1.4.0",
"electron": "5.0.9",
"electron-builder": "20.44.4",
"electron-builder": "20.34.0",
"electron-debug": "1.5.0",
"electron-devtools-installer": "2.2.3",
"electron-log": "2.2.16",
"electron-rebuild": "1.8.6",
"electron-settings": "3.1.4",
"electron-updater": "4.1.2",
"electron-updater": "2.21.4",
"exports-loader": "0.6.3",
"file-loader": "0.10.0",
"fs-extra": "8.1.0",
"gh-pages": "2.0.1",
"html-webpack-plugin": "3.2.0",
"jasmine": "3.4.0",
"jasmine-core": "3.4.0",
"jasmine-node": "3.0.0",
"jasmine-ts": "0.3.0",
"jsonfile": "5.0.0",
"jsonfile": "4.0.0",
"lerna": "3.16.4",
"lodash": "4.17.15",
"node-hid": "0.7.9",
"npm-run-all": "4.1.5",
"npm-run-all": "4.0.2",
"nrf-intel-hex": "1.3.0",
"postcss-url": "8.0.0",
"pre-commit": "1.2.2",
"request": "2.88.0",
"rimraf": "2.6.1",
@@ -91,7 +96,6 @@
"server:electron": "lerna exec --scope uhk-web npm run server:renderer",
"electron": "lerna exec --scope uhk-agent npm start",
"electron:spe": "lerna exec --scope uhk-agent npm run electron:spe",
"electron:kboot": "lerna exec --scope uhk-agent npm run electron:kboot",
"pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites",
"release": "node ./scripts/release.js",

View File

@@ -12,19 +12,15 @@ export class KBoot {
}
open(): void {
logger('Open peripheral');
this.peripheral.open();
}
close(): void {
logger('Close peripheral');
this.peripheral.close();
}
// ================= Read properties ==================
async getProperty(property: Properties, memoryId = MemoryIds.Internal): Promise<CommandResponse> {
logger('Start read memory %o', { property, memoryId });
const command: CommandOption = {
command: Commands.GetProperty,
params: [
@@ -36,17 +32,14 @@ export class KBoot {
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Property) {
logger('Response tag is not property response: %d', property);
throw new Error('Response tag is not property response');
}
if (response.code === ResponseCodes.UnknownProperty) {
logger('Unknown property %d', response.code);
throw new Error('Unknown property!');
}
if (response.code !== ResponseCodes.Success) {
logger('Unknown error %d', response.code);
throw new Error(`Unknown error. Error code:${response.code}`);
}
@@ -54,8 +47,6 @@ export class KBoot {
}
async getBootloaderVersion(): Promise<BootloaderVersion> {
logger('Start to read Bootloader Version');
const response = await this.getProperty(Properties.BootloaderVersion);
const version: BootloaderVersion = {
@@ -73,9 +64,7 @@ export class KBoot {
// ================= End read properties ==================
async flashSecurityDisable(key: number[]): Promise<void> {
logger('Start flash security disable %o', { key });
if (key.length !== 8) {
logger('Error: Flash security key must be 8 byte. %o', key);
throw new Error('Flash security key must be 8 byte');
}
@@ -87,18 +76,15 @@ export class KBoot {
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Can not disable flash security: %d', response.code);
throw new Error(`Can not disable flash security`);
}
}
async flashEraseRegion(startAddress: number, count: number): Promise<void> {
logger('Start flash erase region');
const command: CommandOption = {
command: Commands.FlashEraseRegion,
params: [
@@ -110,18 +96,15 @@ export class KBoot {
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Can not flash erase region: %d', response.code);
throw new Error(`Can not flash erase region`);
throw new Error(`Can not disable flash security`);
}
}
async flashEraseAllUnsecure(): Promise<void> {
logger('Start flash erase all unsecure');
const command: CommandOption = {
command: Commands.FlashEraseAllUnsecure,
params: []
@@ -130,23 +113,19 @@ export class KBoot {
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Can not flash erase all unsecure: %d', response.code);
throw new Error(`Can not flash erase all unsecure`);
throw new Error(`Can not disable flash security`);
}
}
async readMemory(startAddress: number, count: number): Promise<any> {
logger('Start read memory %o', { startAddress, count });
return this.peripheral.readMemory(startAddress, count);
}
async writeMemory(options: DataOption): Promise<void> {
logger('Start write memory %o', { options });
return this.peripheral.writeMemory(options);
}
@@ -154,7 +133,6 @@ export class KBoot {
* Reset the bootloader
*/
async reset(): Promise<void> {
logger('Start reset the bootloader');
const command: CommandOption = {
command: Commands.Reset,
params: []
@@ -176,12 +154,10 @@ export class KBoot {
}
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Unknown error %d', response.code);
throw new Error(`Unknown error. Error code:${response.code}`);
}
}
@@ -192,9 +168,8 @@ export class KBoot {
* @param [speed=64] - Speed of the I2C
*/
async configureI2c(address: number, speed = 64): Promise<void> {
logger('Start configure I2C', { address, speed });
if (address > 127) {
logger('Only 7-bit i2c address is supported');
throw new Error('Only 7-bit i2c address is supported');
}
@@ -209,12 +184,10 @@ export class KBoot {
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Unknown error %d', response.code);
throw new Error(`Unknown error. Error code:${response.code}`);
}
}

View File

@@ -55,8 +55,6 @@ export class UsbPeripheral implements Peripheral {
close(): void {
if (this._device) {
this._device.close();
this._device.removeAllListeners('data');
this._device.removeAllListeners('error');
this._device = undefined;
}
}
@@ -224,7 +222,6 @@ export class UsbPeripheral implements Peripheral {
}
private _readFromBuffer(bufferName: string, byte: number, timeout: number): Promise<Buffer> {
logger('start read from buffer %o', { bufferName, byte, timeout });
return new Promise<Buffer>(async (resolve, reject) => {
const startTime = new Date();
while (startTime.getTime() + timeout > new Date().getTime()) {
@@ -269,7 +266,6 @@ export class UsbPeripheral implements Peripheral {
}
private async _getNextCommandResponse(): Promise<CommandResponse> {
logger('Start read next command response');
const response = await this._readFromCommandStream();
const commandResponse = decodeCommandResponse(response);
logger('next command response: %o', commandResponse);

View File

@@ -29,7 +29,6 @@
"scripts": {
"start": "cross-env DEBUG=kboot* electron ./dist/electron-main.js",
"electron:spe": "electron ./dist/electron-main.js --spe",
"electron:kboot": "cross-env DEBUG=kboot* electron ./dist/electron-main.js --useKboot",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-to-tmp-folder",
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
"lint": "tslint --project tsconfig.json",

View File

@@ -11,7 +11,6 @@ import * as commandLineArgs from 'command-line-args';
import { UhkHidDevice, UhkOperations } from 'uhk-usb';
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
import { CommandLineArgs, LogRegExps } from 'uhk-common';
import { UhkBlhost } from 'uhk-usb';
import { DeviceService } from './services/device.service';
import { logger } from './services/logger.service';
import { AppUpdateService } from './services/app-update.service';
@@ -23,8 +22,7 @@ import { loadWindowState, saveWindowState } from './util/window';
const optionDefinitions = [
{name: 'addons', type: Boolean},
{name: 'spe', type: Boolean}, // simulate privilege escalation error
{name: 'useKboot', type: Boolean} // If it is true use kboot package instead of blhost for firmware upgrade
{name: 'spe', type: Boolean} // simulate privilege escalation error
];
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
@@ -38,7 +36,6 @@ let win: Electron.BrowserWindow;
autoUpdater.logger = logger;
let deviceService: DeviceService;
let uhkBlhost: UhkBlhost;
let uhkHidDeviceService: UhkHidDevice;
let uhkOperations: UhkOperations;
let appUpdateService: AppUpdateService;
@@ -103,9 +100,8 @@ function createWindow() {
setMenu(win);
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
uhkBlhost = new UhkBlhost(logger, packagesDir);
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir, options);
uhkOperations = new UhkOperations(logger, uhkHidDeviceService, packagesDir);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir);
appUpdateService = new AppUpdateService(logger, win, app);
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
sudoService = new SudoService(logger, options);

View File

@@ -1,7 +1,6 @@
import { ipcMain } from 'electron';
import { isEqual } from 'lodash';
import {
CommandLineArgs,
ConfigurationReply,
DeviceConnectionState,
FirmwareUpgradeIpcResponse,
@@ -42,8 +41,7 @@ export class DeviceService {
private win: Electron.BrowserWindow,
private device: UhkHidDevice,
private operations: UhkOperations,
private rootDir: string,
private options: CommandLineArgs) {
private rootDir: string) {
this.startPollUhkDevice();
this.uhkDevicePoller()
.catch(error => {
@@ -184,25 +182,15 @@ export class DeviceService {
const packageJson = await getPackageJsonFromPathAsync(firmwarePathData.packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
if (this.options.useKboot) {
await this.operations.updateRightFirmwareWithKboot(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModuleWithKboot(firmwarePathData.leftFirmwarePath);
} else {
await this.operations.updateRightFirmwareWithBlhost(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModuleWithBlhost(firmwarePathData.leftFirmwarePath);
}
await this.operations.updateRightFirmware(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModule(firmwarePathData.leftFirmwarePath);
} else {
const packageJsonPath = path.join(this.rootDir, 'packages/firmware/package.json');
const packageJson = await getPackageJsonFromPathAsync(packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
if (this.options.useKboot) {
await this.operations.updateRightFirmwareWithKboot();
await this.operations.updateLeftModuleWithKboot();
} else {
await this.operations.updateRightFirmwareWithBlhost();
await this.operations.updateLeftModuleWithBlhost();
}
await this.operations.updateRightFirmware();
await this.operations.updateLeftModule();
}
response.success = true;
@@ -232,11 +220,7 @@ export class DeviceService {
try {
await this.stopPollUhkDevice();
if (this.options.useKboot) {
await this.operations.updateRightFirmwareWithKboot();
} else {
await this.operations.updateRightFirmwareWithBlhost();
}
await this.operations.updateRightFirmware();
response.modules = await this.getHardwareModules(false);
response.success = true;
@@ -286,21 +270,19 @@ export class DeviceService {
while (true) {
if (this._pollerAllowed) {
this._uhkDevicePolling = true;
try {
const state = await this.device.getDeviceConnectionStateAsync();
if (!isEqual(state, savedState)) {
savedState = state;
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
}
} catch (err) {
this.logService.error('[DeviceService] Device connection state query error', err);
this._uhkDevicePolling = true;
const state = await this.device.getDeviceConnectionStateAsync();
if (!isEqual(state, savedState)) {
savedState = state;
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
}
this._uhkDevicePolling = false;
}
this._uhkDevicePolling = false;
await snooze(250);
}
}

View File

@@ -7,8 +7,4 @@ export interface CommandLineArgs {
* simulate privilege escalation error
*/
spe?: boolean;
/**
* If it is true use kboot package instead of blhost for firmware upgrade
*/
useKboot?: boolean;
}

View File

@@ -71,9 +71,9 @@ export enum EnumerationNameToProductId {
}
export enum ModuleSlotToI2cAddress {
leftHalf = '0x10',
leftModule = '0x20',
rightModule = '0x30'
leftHalf = 0x10,
leftModule = 0x20,
rightModule = 0x30
}
export enum ModuleSlotToId {

View File

@@ -1,5 +1,4 @@
export * from './constants';
export * from './uhk-blhost';
export * from './uhk-hid-device';
export * from './uhk-operations';
export * from './util';

View File

@@ -1,89 +0,0 @@
import * as path from 'path';
import { spawn } from 'child_process';
import { LogService } from 'uhk-common';
import { retry } from './util';
export class UhkBlhost {
private blhostPath: string;
constructor(private logService: LogService,
private rootDir: string) {
}
public async runBlhostCommand(params: Array<string>): Promise<void> {
const self = this;
return new Promise<void>((resolve, reject) => {
const blhostPath = this.getBlhostPath();
self.logService.debug(`[blhost] RUN: ${blhostPath} ${params.join(' ')}`);
const childProcess = spawn(`"${blhostPath}"`, params, {shell: true});
let finished = false;
childProcess.stdout.on('data', data => {
self.logService.debug(`[blhost] STDOUT: ${data}`);
});
childProcess.stderr.on('data', data => {
self.logService.error(`[blhost] STDERR: ${data}`);
});
childProcess.on('close', code => {
self.logService.debug(`[blhost] CLOSE_CODE: ${code}`);
finish(code);
});
childProcess.on('exit', code => {
self.logService.debug(`[blhost] EXIT_CODE: ${code}`);
finish(code);
});
childProcess.on('error', err => {
self.logService.debug(`[blhost] ERROR: ${err}`);
});
function finish(code) {
if (finished) {
return;
}
finished = true;
self.logService.debug(`[blhost] FINISHED: ${code}`);
if (code !== 0) {
return reject(new Error(`blhost error code:${code}`));
}
resolve();
}
});
}
public async runBlhostCommandRetry(params: Array<string>, maxTry = 100): Promise<void> {
return await retry(async () => await this.runBlhostCommand(params), maxTry, this.logService);
}
private getBlhostPath(): string {
if (this.blhostPath) {
return this.blhostPath;
}
let blhostPath;
switch (process.platform) {
case 'linux':
blhostPath = 'linux/x86_64/blhost';
break;
case 'darwin':
blhostPath = 'mac/blhost';
break;
case 'win32':
blhostPath = 'win/blhost.exe';
break;
default:
throw new Error(`Could not find blhost path. Unknown platform:${process.platform}`);
}
this.blhostPath = path.join(this.rootDir, `packages/blhost/${blhostPath}`);
return this.blhostPath;
}
}

View File

@@ -208,7 +208,7 @@ export class UhkHidDevice {
while (new Date().getTime() - startTime.getTime() < 20000) {
const devs = devices();
this.logService.debug('[UhkHidDevice] reenumeration devices', devs);
this.logService.silly('[UhkHidDevice] reenumeration devices', devs);
const inBootloaderMode = devs.some((x: Device) =>
x.vendorId === Constants.VENDOR_ID &&
@@ -219,7 +219,7 @@ export class UhkHidDevice {
return;
}
this.logService.debug(`[UhkHidDevice] Could not find reenumerated device: ${reenumMode}. Waiting...`);
this.logService.silly(`[UhkHidDevice] Could not find reenumerated device: ${reenumMode}. Waiting...`);
await snooze(100);
if (!jumped) {
@@ -232,7 +232,7 @@ export class UhkHidDevice {
device.close();
jumped = true;
} else {
this.logService.debug(`[UhkHidDevice] USB[T]: Enumerate device is not ready yet}`);
this.logService.silly(`[UhkHidDevice] USB[T]: Enumerate device is not ready yet}`);
}
}
}
@@ -249,7 +249,7 @@ export class UhkHidDevice {
if (command === KbootCommands.idle) {
transfer = Buffer.from([UsbCommand.SendKbootCommandToModule, command]);
} else {
transfer = Buffer.from([UsbCommand.SendKbootCommandToModule, command, Number.parseInt(module, 16)]);
transfer = Buffer.from([UsbCommand.SendKbootCommandToModule, command, module]);
}
await retry(async () => await this.write(transfer), maxTry, this.logService);
}

View File

@@ -13,7 +13,6 @@ import {
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import { UhkBlhost } from './uhk-blhost';
import { UhkHidDevice } from './uhk-hid-device';
import { readBootloaderFirmwareFromHexFileAsync, snooze, waitForDevice } from './util';
import { ConfigBufferId, convertBufferToIntArray, DevicePropertyIds, getTransferBuffers, UsbCommand } from '../index';
@@ -21,67 +20,11 @@ import { LoadConfigurationsResult } from './models/load-configurations-result';
export class UhkOperations {
constructor(private logService: LogService,
private blhost: UhkBlhost,
private device: UhkHidDevice,
private rootDir: string) {
}
public async updateRightFirmwareWithBlhost(firmwarePath = this.getFirmwarePath()) {
this.logService.debug(`[UhkOperations] Operating system: ${os.type()} ${os.release()} ${os.arch()}`);
this.logService.debug('[UhkOperations] Start flashing right firmware');
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.bootloader.toString(16)}`];
await this.device.reenumerate(EnumerationModes.Bootloader);
this.device.close();
await this.blhost.runBlhostCommand([...prefix, 'flash-security-disable', '0403020108070605']);
await this.blhost.runBlhostCommand([...prefix, 'flash-erase-region', '0xc000', '475136']);
await this.blhost.runBlhostCommand([...prefix, 'flash-image', `"${firmwarePath}"`]);
await this.blhost.runBlhostCommand([...prefix, 'reset']);
this.logService.debug('[UhkOperations] Right firmware successfully flashed');
}
public async updateLeftModuleWithBlhost(firmwarePath = this.getLeftModuleFirmwarePath()) {
this.logService.debug('[UhkOperations] Start flashing left module firmware');
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.buspal.toString(16)}`];
const buspalPrefix = [...prefix, `--buspal i2c,${ModuleSlotToI2cAddress.leftHalf}`];
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
this.device.close();
await snooze(1000);
await this.device.sendKbootCommandToModule(ModuleSlotToI2cAddress.leftHalf, KbootCommands.ping, 100);
await snooze(1000);
await this.device.jumpToBootloaderModule(ModuleSlotToId.leftHalf);
this.device.close();
const leftModuleBricked = await this.waitForKbootIdle();
if (!leftModuleBricked) {
const msg = '[UhkOperations] Couldn\'t connect to the left keyboard half.';
this.logService.error(msg);
throw new Error(msg);
}
await this.device.reenumerate(EnumerationModes.Buspal);
this.device.close();
await this.blhost.runBlhostCommandRetry([...buspalPrefix, 'get-property', '1']);
await this.blhost.runBlhostCommand([...buspalPrefix, 'flash-erase-all-unsecure']);
await this.blhost.runBlhostCommand([...buspalPrefix, 'write-memory', '0x0', `"${firmwarePath}"`]);
await this.blhost.runBlhostCommand([...prefix, 'reset']);
await snooze(1000);
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
this.device.close();
await snooze(1000);
await this.device.sendKbootCommandToModule(ModuleSlotToI2cAddress.leftHalf, KbootCommands.reset, 100);
this.device.close();
await snooze(1000);
await this.device.sendKbootCommandToModule(ModuleSlotToI2cAddress.leftHalf, KbootCommands.idle);
this.device.close();
this.logService.debug('[UhkOperations] Left firmware successfully flashed');
this.logService.debug('[UhkOperations] Both left and right firmwares successfully flashed');
}
public async updateRightFirmwareWithKboot(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');
@@ -115,10 +58,9 @@ export class UhkOperations {
this.logService.debug('[UhkOperations] Right firmware successfully flashed');
}
public async updateLeftModuleWithKboot(firmwarePath = this.getLeftModuleFirmwarePath()) {
public async updateLeftModule(firmwarePath = this.getLeftModuleFirmwarePath()) {
this.logService.debug('[UhkOperations] Start flashing left module firmware');
const i2cAddressOfLeftModule = Number.parseInt(ModuleSlotToI2cAddress.leftHalf, 16);
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
this.device.close();
await snooze(1000);
@@ -144,31 +86,28 @@ export class UhkOperations {
while (true) {
try {
this.logService.debug('[UhkOperations] Try to connect to the LEFT keyboard');
await kboot.configureI2c(i2cAddressOfLeftModule);
await kboot.configureI2c(ModuleSlotToI2cAddress.leftHalf);
await kboot.getProperty(Properties.BootloaderVersion);
break;
} catch {
if (tryCount > 100) {
throw new Error('Can not connect to the LEFT keyboard');
}
await snooze(2000);
} finally {
kboot.close();
}
await snooze(100);
tryCount++;
}
// https://github.com/node-hid/node-hid/issues/230
this.logService.debug('[UhkOperations] Wait 1 sec to prevent node-hid race condition');
await snooze(1000);
this.logService.debug('[UhkOperations] Flash erase all on LEFT keyboard');
await kboot.configureI2c(i2cAddressOfLeftModule);
await kboot.configureI2c(ModuleSlotToI2cAddress.leftHalf);
await kboot.flashEraseAllUnsecure();
this.logService.debug('[UhkOperations] Read LEFT firmware from file');
const configData = fs.readFileSync(firmwarePath);
this.logService.debug('[UhkOperations] Write memory');
await kboot.configureI2c(i2cAddressOfLeftModule);
await kboot.configureI2c(ModuleSlotToI2cAddress.leftHalf);
await kboot.writeMemory({ startAddress: 0, data: configData });
this.logService.debug('[UhkOperations] Reset LEFT keyboard');

View File

@@ -1,7 +1,7 @@
import { Device, devices } from 'node-hid';
import { readFile } from 'fs-extra';
import { EOL } from 'os';
import * as MemoryMap from 'nrf-intel-hex';
import MemoryMap from 'nrf-intel-hex';
import { LogService } from 'uhk-common';
import { Constants, UsbCommand } from './constants';
@@ -126,9 +126,7 @@ export const getFileContentAsync = async (filePath: string): Promise<Array<strin
export const readBootloaderFirmwareFromHexFileAsync = async (hexFilePath: string): Promise<Map<any, any>> => {
const fileContent = await readFile(hexFilePath, { encoding: 'utf8' });
const fromHex = MemoryMap.fromHex ? MemoryMap.fromHex : MemoryMap.default.fromHex;
const memoryMap = fromHex(fileContent);
const memoryMap = MemoryMap.fromHex(fileContent);
return memoryMap;
};

File diff suppressed because it is too large Load Diff

View File

@@ -19,22 +19,22 @@
},
"private": true,
"devDependencies": {
"@angular/animations": "8.2.6",
"@angular-builders/custom-webpack": "8.2.0",
"@angular/cli": "8.3.4",
"@angular/common": "8.2.6",
"@angular/compiler": "8.2.6",
"@angular/compiler-cli": "8.2.6",
"@angular/core": "8.2.6",
"@angular-devkit/build-angular": "0.803.4",
"@angular-devkit/build-optimizer": "0.803.4",
"@angular-devkit/core": "8.3.4",
"@angular/forms": "8.2.6",
"@angular/language-service": "8.2.6",
"@angular/platform-browser": "8.2.6",
"@angular/platform-browser-dynamic": "8.2.6",
"@angular/router": "8.2.6",
"@ngtools/webpack": "8.3.4",
"@angular/animations": "8.2.3",
"@angular-builders/custom-webpack": "8.1.0",
"@angular/cli": "8.2.1",
"@angular/common": "8.2.3",
"@angular/compiler": "8.2.3",
"@angular/compiler-cli": "8.2.3",
"@angular/core": "8.2.3",
"@angular-devkit/build-angular": "0.802.1",
"@angular-devkit/build-optimizer": "0.802.1",
"@angular-devkit/core": "8.2.1",
"@angular/forms": "8.2.3",
"@angular/language-service": "8.2.3",
"@angular/platform-browser": "8.2.3",
"@angular/platform-browser-dynamic": "8.2.3",
"@angular/router": "8.2.3",
"@ngtools/webpack": "8.2.1",
"@ngrx/effects": "8.2.0",
"@ngrx/router-store": "8.2.0",
"@ngrx/store": "8.2.0",

View File

@@ -16,7 +16,6 @@ import {
} from './store';
import { ProgressButtonState } from './store/reducers/progress-button-state';
import { UpdateInfo } from './models/update-info';
import { KeyUpAction, KeyDownAction } from './store/actions/app';
@Component({
selector: 'main-app',
@@ -96,13 +95,6 @@ export class MainAppComponent implements OnDestroy {
this.enableUsbStackTest();
event.preventDefault();
}
this.store.dispatch(new KeyDownAction(event));
}
@HostListener('document:keyup', ['$event'])
onKeyUp(event: KeyboardEvent) {
this.store.dispatch(new KeyUpAction(event));
}
updateApp() {

View File

@@ -20,7 +20,6 @@
<li>Right click on a key: Capture key</li>
<li>Hold Shift while clicking on a key: Remap on all keymaps</li>
<li>Hold Alt while clicking on a key: Remap on all layers</li>
<li>Hold Alt to see macro reference counts in the side menu</li>
</ul>
</div>
</div>

View File

@@ -91,12 +91,6 @@
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/macro', macro.id]"
[class.disabled]="state.updatingFirmware">{{macro.name}}</a>
<span *ngIf="state.macroUsageCountVisible"
class="sidebar__macro_count badge"
title="This is the number of times the macro is used across all keymaps."
data-toggle="tooltip"
data-placement="bottom"
data-container="body">{{ macro.usageCount }}</span>
</div>
</li>
</ul>

View File

@@ -148,12 +148,6 @@ ul {
right: 19px;
top: 3px;
}
&__macro_count {
position: absolute;
right: 11px;
top: 1px;
}
}
.menu--bottom {

View File

@@ -1,3 +1 @@
export * from './last-edited-key';
export * from './macro-menu-item';
export * from './side-menu-page-state';

View File

@@ -1,5 +0,0 @@
export interface MacroMenuItem {
id: number;
name: string;
usageCount: number;
}

View File

@@ -1,5 +1,4 @@
import { Keymap, Macro } from 'uhk-common';
import { MacroMenuItem } from './macro-menu-item';
export interface SideMenuPageState {
showAddonMenu: boolean;
@@ -7,7 +6,6 @@ export interface SideMenuPageState {
updatingFirmware: boolean;
deviceName: string;
keymaps: Keymap[];
macros: MacroMenuItem[];
macros: Macro[];
restoreUserConfiguration: boolean;
macroUsageCountVisible: boolean;
}

View File

@@ -19,9 +19,7 @@ export enum ActionTypes {
SetupPermissionError = '[app] Setup permission error',
LoadAppStartInfo = '[app] Load app start info',
StartKeypressCapturing = '[app] Start keypress capturing',
StopKeypressCapturing = '[app] Stop keypress capturing',
KeyDown = '[app] Key down',
KeyUp = '[app] Key up'
StopKeypressCapturing = '[app] Stop keypress capturing'
}
export class AppBootstrappedAction implements Action {
@@ -112,18 +110,6 @@ export class StopKeypressCapturingAction implements Action {
type = ActionTypes.StopKeypressCapturing;
}
export class KeyDownAction implements Action {
readonly type = ActionTypes.KeyDown;
constructor(public payload: KeyboardEvent) {}
}
export class KeyUpAction implements Action {
readonly type = ActionTypes.KeyUp;
constructor(public payload: KeyboardEvent) {}
}
export type Actions
= AppStartedAction
| AppBootstrappedAction
@@ -141,6 +127,4 @@ export type Actions
| LoadAppStartInfoAction
| StartKeypressCapturingAction
| StopKeypressCapturingAction
| KeyDownAction
| KeyUpAction
;

View File

@@ -1,7 +1,7 @@
import { ActionReducerMap, createSelector, MetaReducer } from '@ngrx/store';
import { routerReducer, RouterReducerState } from '@ngrx/router-store';
import { storeFreeze } from 'ngrx-store-freeze';
import { HardwareModules, Keymap, UserConfiguration, PlayMacroAction } from 'uhk-common';
import { HardwareModules, Keymap, UserConfiguration } from 'uhk-common';
import * as fromUserConfig from './reducers/user-configuration';
import * as fromPreset from './reducers/preset';
@@ -16,7 +16,6 @@ import { environment } from '../../environments/environment';
import { RouterStateUrl } from './router-util';
import { PrivilagePageSate } from '../models/privilage-page-sate';
import { isVersionGte } from '../util';
import { SideMenuPageState, MacroMenuItem } from '../models';
// State interface for the application
export interface AppState {
@@ -68,7 +67,6 @@ export const deviceConfigurationLoaded = createSelector(appState, fromApp.device
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem);
export const keypressCapturing = createSelector(appState, fromApp.keypressCapturing);
export const getMacroUsageCountVisible = createSelector(appState, fromApp.macroUsageCountVisible);
export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows);
export const contributors = (state: AppState) => state.contributors;
export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindows, notSupportedOs => !notSupportedOs);
@@ -125,57 +123,25 @@ export const getPrivilegePageState = createSelector(appState, getUpdateUdevRules
};
});
export const getMacroMenuItems = (userConfiguration: UserConfiguration): MacroMenuItem[] => {
const macroMap = userConfiguration.macros.reduce((map, macro) => {
return map.set(macro.id, {
id: macro.id,
name: macro.name,
usageCount: 0
});
}, new Map<number, MacroMenuItem>());
for (const keymap of userConfiguration.keymaps) {
for (const layer of keymap.layers) {
for (const module of layer.modules) {
for (const keyAction of module.keyActions) {
if (!(keyAction instanceof PlayMacroAction)) {
continue;
}
const menuItem = macroMap.get(keyAction.macroId);
menuItem.usageCount++;
}
}
}
}
return Array
.from(macroMap.values())
.sort((first: MacroMenuItem, second: MacroMenuItem) => first.name.localeCompare(second.name));
};
export const getSideMenuPageState = createSelector(
showAddonMenu,
runningInElectron,
updatingFirmware,
getUserConfiguration,
getRestoreUserConfiguration,
getMacroUsageCountVisible,
(showAddonMenuValue: boolean,
runningInElectronValue: boolean,
updatingFirmwareValue: boolean,
userConfiguration: UserConfiguration,
restoreUserConfiguration: boolean,
macroUsageCountVisible): SideMenuPageState => {
restoreUserConfiguration: boolean) => {
return {
showAddonMenu: showAddonMenuValue,
runInElectron: runningInElectronValue,
updatingFirmware: updatingFirmwareValue,
deviceName: userConfiguration.deviceName,
keymaps: userConfiguration.keymaps,
macros: getMacroMenuItems(userConfiguration),
restoreUserConfiguration,
macroUsageCountVisible
macros: userConfiguration.macros,
restoreUserConfiguration
};
}
);

View File

@@ -30,7 +30,6 @@ export interface State {
platform?: string;
osVersion?: string;
keypressCapturing: boolean;
macroUsageCountVisible: boolean;
}
export const initialState: State = {
@@ -41,8 +40,7 @@ export const initialState: State = {
configLoading: true,
agentVersionInfo: getVersions(),
privilegeWhatWillThisDoClicked: false,
keypressCapturing: false,
macroUsageCountVisible: false
keypressCapturing: false
};
export function reducer(
@@ -158,8 +156,7 @@ export function reducer(
case App.ActionTypes.StartKeypressCapturing:
return {
...state,
keypressCapturing: true,
macroUsageCountVisible: false
keypressCapturing: true
};
case App.ActionTypes.StopKeypressCapturing:
@@ -168,24 +165,6 @@ export function reducer(
keypressCapturing: false
};
case App.ActionTypes.KeyDown: {
const event = (action as App.KeyDownAction).payload;
return {
...state,
macroUsageCountVisible: !state.keypressCapturing && !event.defaultPrevented && event.altKey
};
}
case App.ActionTypes.KeyUp: {
const event = (action as App.KeyDownAction).payload;
return {
...state,
macroUsageCountVisible: event.altKey
};
}
default:
return state;
}
@@ -218,4 +197,3 @@ export const runningOnNotSupportedWindows = (state: State): boolean => {
};
export const keypressCapturing = (state: State): boolean => state.keypressCapturing;
export const macroUsageCountVisible = (state: State): boolean => state.macroUsageCountVisible;

View File

@@ -7,18 +7,9 @@ const rootDir = path.join(__dirname, '../../tmp');
const uhkHidDevice = new UhkHidDevice(logService, {}, rootDir);
const uhkOperations = new UhkOperations(logService, uhkHidDevice, rootDir);
process.on('uncaughtException', error => {
console.error('uncaughtException', error);
process.exit(1);
});
process.on('unhandledRejection', (reason: any, promise: Promise<any>): void => {
console.error('unhandledRejection', { reason, promise });
});
uhkOperations
.updateRightFirmwareWithKboot()
.then(() => uhkOperations.updateLeftModuleWithKboot())
.updateRightFirmware()
.then(() => uhkOperations.updateLeftModule())
.then(() => console.log('Firmware upgrade finished'))
.catch(error => {
console.error(error);

View File

@@ -8,13 +8,6 @@ const copyOptions = {
const promises = [];
promises.push(
fse.copy(
path.join(__dirname, '../packages/usb/blhost'),
path.join(__dirname, '../tmp/packages/blhost'),
copyOptions)
);
promises.push(
fse.copy(
path.join(__dirname, '../rules'),

View File

@@ -96,7 +96,7 @@ if (TEST_BUILD || gitTag) {
const rootJson = require('../package.json');
update2ndPackageJson(rootJson);
// Add firmware and blhost to extra resources
// Add firmware to extra resources
const extractedFirmwareDir = path.join(__dirname, '../tmp/packages');
extraResources.push({from: extractedFirmwareDir, to: 'packages/'});