Compare commits
2 Commits
master
...
chore-rebu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3e21ee9e4 | ||
|
|
61ddb49b0f |
@@ -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.
|
- Implement the Kinetis bootloader protocol natively instead of relying on blhost.
|
||||||
- Fix device recovery mode.
|
- Fix device recovery mode.
|
||||||
- Correctly display whether the UHK is detected.
|
- 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.
|
- 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.
|
- Don't change tab immediately upon closing the key action popover.
|
||||||
- Fix UI glitch that occurrs when hitting Tab after updating keymap description.
|
- 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.
|
- Make the Agent icon slightly smaller to be consistent with most application icons.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Agent is the configuration application of the [Ultimate Hacking Keyboard](https:
|
|||||||
|
|
||||||
### Step 1: Build Dependencies
|
### 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`.
|
You'll also need `libusb`.
|
||||||
On debian-based linux distros, `apt-get install libusb-dev libudev-dev g++` is sufficient.
|
On debian-based linux distros, `apt-get install libusb-dev libudev-dev g++` is sufficient.
|
||||||
|
|||||||
3616
package-lock.json
generated
3616
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@@ -4,9 +4,9 @@
|
|||||||
"author": "Ultimate Gadget Laboratories",
|
"author": "Ultimate Gadget Laboratories",
|
||||||
"main": "electron/dist/electron-main.js",
|
"main": "electron/dist/electron-main.js",
|
||||||
"version": "1.2.13",
|
"version": "1.2.13",
|
||||||
"firmwareVersion": "8.6.0",
|
"firmwareVersion": "8.5.4",
|
||||||
"deviceProtocolVersion": "4.5.0",
|
"deviceProtocolVersion": "4.4.0",
|
||||||
"userConfigVersion": "4.1.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.",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -27,15 +27,16 @@
|
|||||||
"@types/jasmine": "3.3.12",
|
"@types/jasmine": "3.3.12",
|
||||||
"@types/jasminewd2": "2.0.3",
|
"@types/jasminewd2": "2.0.3",
|
||||||
"@types/jquery": "3.3.29",
|
"@types/jquery": "3.3.29",
|
||||||
"@types/jsonfile": "5.0.0",
|
"@types/jsonfile": "4.0.1",
|
||||||
"@types/lodash": "4.14.136",
|
"@types/lodash": "4.14.136",
|
||||||
"@types/node": "8.0.53",
|
"@types/node": "8.0.53",
|
||||||
"@types/node-hid": "0.7.2",
|
"@types/node-hid": "0.7.0",
|
||||||
"@types/request": "2.0.8",
|
"@types/request": "2.0.8",
|
||||||
"@types/semver": "5.5.0",
|
"@types/semver": "5.5.0",
|
||||||
"@types/tmp": "0.0.33",
|
"@types/tmp": "0.0.33",
|
||||||
|
"autoprefixer": "6.5.3",
|
||||||
"buffer": "5.0.6",
|
"buffer": "5.0.6",
|
||||||
"check-node-version": "4.0.1",
|
"check-node-version": "^3.2.0",
|
||||||
"copy-webpack-plugin": "5.0.0",
|
"copy-webpack-plugin": "5.0.0",
|
||||||
"copyfiles": "2.1.1",
|
"copyfiles": "2.1.1",
|
||||||
"core-js": "2.4.1",
|
"core-js": "2.4.1",
|
||||||
@@ -44,25 +45,29 @@
|
|||||||
"decompress-tarbz2": "4.1.1",
|
"decompress-tarbz2": "4.1.1",
|
||||||
"devtron": "1.4.0",
|
"devtron": "1.4.0",
|
||||||
"electron": "5.0.9",
|
"electron": "5.0.9",
|
||||||
"electron-builder": "20.44.4",
|
"electron-builder": "20.34.0",
|
||||||
"electron-debug": "1.5.0",
|
"electron-debug": "1.5.0",
|
||||||
"electron-devtools-installer": "2.2.3",
|
"electron-devtools-installer": "2.2.3",
|
||||||
"electron-log": "2.2.16",
|
"electron-log": "2.2.16",
|
||||||
"electron-rebuild": "1.8.6",
|
"electron-rebuild": "1.8.6",
|
||||||
"electron-settings": "3.1.4",
|
"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",
|
"fs-extra": "8.1.0",
|
||||||
"gh-pages": "2.0.1",
|
"gh-pages": "2.0.1",
|
||||||
|
"html-webpack-plugin": "3.2.0",
|
||||||
"jasmine": "3.4.0",
|
"jasmine": "3.4.0",
|
||||||
"jasmine-core": "3.4.0",
|
"jasmine-core": "3.4.0",
|
||||||
"jasmine-node": "3.0.0",
|
"jasmine-node": "3.0.0",
|
||||||
"jasmine-ts": "0.3.0",
|
"jasmine-ts": "0.3.0",
|
||||||
"jsonfile": "5.0.0",
|
"jsonfile": "4.0.0",
|
||||||
"lerna": "3.16.4",
|
"lerna": "3.16.4",
|
||||||
"lodash": "4.17.15",
|
"lodash": "4.17.15",
|
||||||
"node-hid": "0.7.9",
|
"node-hid": "0.7.9",
|
||||||
"npm-run-all": "4.1.5",
|
"npm-run-all": "4.0.2",
|
||||||
"nrf-intel-hex": "1.3.0",
|
"nrf-intel-hex": "1.3.0",
|
||||||
|
"postcss-url": "8.0.0",
|
||||||
"pre-commit": "1.2.2",
|
"pre-commit": "1.2.2",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"rimraf": "2.6.1",
|
"rimraf": "2.6.1",
|
||||||
@@ -91,7 +96,6 @@
|
|||||||
"server:electron": "lerna exec --scope uhk-web npm run server:renderer",
|
"server:electron": "lerna exec --scope uhk-web npm run server:renderer",
|
||||||
"electron": "lerna exec --scope uhk-agent npm start",
|
"electron": "lerna exec --scope uhk-agent npm start",
|
||||||
"electron:spe": "lerna exec --scope uhk-agent npm run electron:spe",
|
"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",
|
"pack": "node ./scripts/release.js",
|
||||||
"sprites": "node ./scripts/generate-svg-sprites",
|
"sprites": "node ./scripts/generate-svg-sprites",
|
||||||
"release": "node ./scripts/release.js",
|
"release": "node ./scripts/release.js",
|
||||||
|
|||||||
@@ -12,19 +12,15 @@ export class KBoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open(): void {
|
open(): void {
|
||||||
logger('Open peripheral');
|
|
||||||
this.peripheral.open();
|
this.peripheral.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
logger('Close peripheral');
|
|
||||||
this.peripheral.close();
|
this.peripheral.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= Read properties ==================
|
// ================= Read properties ==================
|
||||||
async getProperty(property: Properties, memoryId = MemoryIds.Internal): Promise<CommandResponse> {
|
async getProperty(property: Properties, memoryId = MemoryIds.Internal): Promise<CommandResponse> {
|
||||||
logger('Start read memory %o', { property, memoryId });
|
|
||||||
|
|
||||||
const command: CommandOption = {
|
const command: CommandOption = {
|
||||||
command: Commands.GetProperty,
|
command: Commands.GetProperty,
|
||||||
params: [
|
params: [
|
||||||
@@ -36,17 +32,14 @@ export class KBoot {
|
|||||||
const response = await this.peripheral.sendCommand(command);
|
const response = await this.peripheral.sendCommand(command);
|
||||||
|
|
||||||
if (response.tag !== ResponseTags.Property) {
|
if (response.tag !== ResponseTags.Property) {
|
||||||
logger('Response tag is not property response: %d', property);
|
|
||||||
throw new Error('Response tag is not property response');
|
throw new Error('Response tag is not property response');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code === ResponseCodes.UnknownProperty) {
|
if (response.code === ResponseCodes.UnknownProperty) {
|
||||||
logger('Unknown property %d', response.code);
|
|
||||||
throw new Error('Unknown property!');
|
throw new Error('Unknown property!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code !== ResponseCodes.Success) {
|
if (response.code !== ResponseCodes.Success) {
|
||||||
logger('Unknown error %d', response.code);
|
|
||||||
throw new Error(`Unknown error. Error code:${response.code}`);
|
throw new Error(`Unknown error. Error code:${response.code}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +47,6 @@ export class KBoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getBootloaderVersion(): Promise<BootloaderVersion> {
|
async getBootloaderVersion(): Promise<BootloaderVersion> {
|
||||||
logger('Start to read Bootloader Version');
|
|
||||||
|
|
||||||
const response = await this.getProperty(Properties.BootloaderVersion);
|
const response = await this.getProperty(Properties.BootloaderVersion);
|
||||||
|
|
||||||
const version: BootloaderVersion = {
|
const version: BootloaderVersion = {
|
||||||
@@ -73,9 +64,7 @@ export class KBoot {
|
|||||||
// ================= End read properties ==================
|
// ================= End read properties ==================
|
||||||
|
|
||||||
async flashSecurityDisable(key: number[]): Promise<void> {
|
async flashSecurityDisable(key: number[]): Promise<void> {
|
||||||
logger('Start flash security disable %o', { key });
|
|
||||||
if (key.length !== 8) {
|
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');
|
throw new Error('Flash security key must be 8 byte');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,18 +76,15 @@ export class KBoot {
|
|||||||
const response = await this.peripheral.sendCommand(command);
|
const response = await this.peripheral.sendCommand(command);
|
||||||
|
|
||||||
if (response.tag !== ResponseTags.Generic) {
|
if (response.tag !== ResponseTags.Generic) {
|
||||||
logger('Response tag is not generic response: %d', response.tag);
|
|
||||||
throw new Error('Response tag is not generic response');
|
throw new Error('Response tag is not generic response');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code !== ResponseCodes.Success) {
|
if (response.code !== ResponseCodes.Success) {
|
||||||
logger('Can not disable flash security: %d', response.code);
|
|
||||||
throw new Error(`Can not disable flash security`);
|
throw new Error(`Can not disable flash security`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async flashEraseRegion(startAddress: number, count: number): Promise<void> {
|
async flashEraseRegion(startAddress: number, count: number): Promise<void> {
|
||||||
logger('Start flash erase region');
|
|
||||||
const command: CommandOption = {
|
const command: CommandOption = {
|
||||||
command: Commands.FlashEraseRegion,
|
command: Commands.FlashEraseRegion,
|
||||||
params: [
|
params: [
|
||||||
@@ -110,18 +96,15 @@ export class KBoot {
|
|||||||
const response = await this.peripheral.sendCommand(command);
|
const response = await this.peripheral.sendCommand(command);
|
||||||
|
|
||||||
if (response.tag !== ResponseTags.Generic) {
|
if (response.tag !== ResponseTags.Generic) {
|
||||||
logger('Response tag is not generic response: %d', response.tag);
|
|
||||||
throw new Error('Response tag is not generic response');
|
throw new Error('Response tag is not generic response');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code !== ResponseCodes.Success) {
|
if (response.code !== ResponseCodes.Success) {
|
||||||
logger('Can not flash erase region: %d', response.code);
|
throw new Error(`Can not disable flash security`);
|
||||||
throw new Error(`Can not flash erase region`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async flashEraseAllUnsecure(): Promise<void> {
|
async flashEraseAllUnsecure(): Promise<void> {
|
||||||
logger('Start flash erase all unsecure');
|
|
||||||
const command: CommandOption = {
|
const command: CommandOption = {
|
||||||
command: Commands.FlashEraseAllUnsecure,
|
command: Commands.FlashEraseAllUnsecure,
|
||||||
params: []
|
params: []
|
||||||
@@ -130,23 +113,19 @@ export class KBoot {
|
|||||||
const response = await this.peripheral.sendCommand(command);
|
const response = await this.peripheral.sendCommand(command);
|
||||||
|
|
||||||
if (response.tag !== ResponseTags.Generic) {
|
if (response.tag !== ResponseTags.Generic) {
|
||||||
logger('Response tag is not generic response: %d', response.tag);
|
|
||||||
throw new Error('Response tag is not generic response');
|
throw new Error('Response tag is not generic response');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code !== ResponseCodes.Success) {
|
if (response.code !== ResponseCodes.Success) {
|
||||||
logger('Can not flash erase all unsecure: %d', response.code);
|
throw new Error(`Can not disable flash security`);
|
||||||
throw new Error(`Can not flash erase all unsecure`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async readMemory(startAddress: number, count: number): Promise<any> {
|
async readMemory(startAddress: number, count: number): Promise<any> {
|
||||||
logger('Start read memory %o', { startAddress, count });
|
|
||||||
return this.peripheral.readMemory(startAddress, count);
|
return this.peripheral.readMemory(startAddress, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
async writeMemory(options: DataOption): Promise<void> {
|
async writeMemory(options: DataOption): Promise<void> {
|
||||||
logger('Start write memory %o', { options });
|
|
||||||
return this.peripheral.writeMemory(options);
|
return this.peripheral.writeMemory(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +133,6 @@ export class KBoot {
|
|||||||
* Reset the bootloader
|
* Reset the bootloader
|
||||||
*/
|
*/
|
||||||
async reset(): Promise<void> {
|
async reset(): Promise<void> {
|
||||||
logger('Start reset the bootloader');
|
|
||||||
const command: CommandOption = {
|
const command: CommandOption = {
|
||||||
command: Commands.Reset,
|
command: Commands.Reset,
|
||||||
params: []
|
params: []
|
||||||
@@ -176,12 +154,10 @@ export class KBoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response.tag !== ResponseTags.Generic) {
|
if (response.tag !== ResponseTags.Generic) {
|
||||||
logger('Response tag is not generic response: %d', response.tag);
|
|
||||||
throw new Error('Response tag is not generic response');
|
throw new Error('Response tag is not generic response');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code !== ResponseCodes.Success) {
|
if (response.code !== ResponseCodes.Success) {
|
||||||
logger('Unknown error %d', response.code);
|
|
||||||
throw new Error(`Unknown error. Error code:${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
|
* @param [speed=64] - Speed of the I2C
|
||||||
*/
|
*/
|
||||||
async configureI2c(address: number, speed = 64): Promise<void> {
|
async configureI2c(address: number, speed = 64): Promise<void> {
|
||||||
logger('Start configure I2C', { address, speed });
|
|
||||||
if (address > 127) {
|
if (address > 127) {
|
||||||
logger('Only 7-bit i2c address is supported');
|
|
||||||
throw new Error('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);
|
const response = await this.peripheral.sendCommand(command);
|
||||||
|
|
||||||
if (response.tag !== ResponseTags.Generic) {
|
if (response.tag !== ResponseTags.Generic) {
|
||||||
logger('Response tag is not generic response: %d', response.tag);
|
|
||||||
throw new Error('Response tag is not generic response');
|
throw new Error('Response tag is not generic response');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code !== ResponseCodes.Success) {
|
if (response.code !== ResponseCodes.Success) {
|
||||||
logger('Unknown error %d', response.code);
|
|
||||||
throw new Error(`Unknown error. Error code:${response.code}`);
|
throw new Error(`Unknown error. Error code:${response.code}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ export class UsbPeripheral implements Peripheral {
|
|||||||
close(): void {
|
close(): void {
|
||||||
if (this._device) {
|
if (this._device) {
|
||||||
this._device.close();
|
this._device.close();
|
||||||
this._device.removeAllListeners('data');
|
|
||||||
this._device.removeAllListeners('error');
|
|
||||||
this._device = undefined;
|
this._device = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +222,6 @@ export class UsbPeripheral implements Peripheral {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _readFromBuffer(bufferName: string, byte: number, timeout: number): Promise<Buffer> {
|
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) => {
|
return new Promise<Buffer>(async (resolve, reject) => {
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
while (startTime.getTime() + timeout > new Date().getTime()) {
|
while (startTime.getTime() + timeout > new Date().getTime()) {
|
||||||
@@ -269,7 +266,6 @@ export class UsbPeripheral implements Peripheral {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _getNextCommandResponse(): Promise<CommandResponse> {
|
private async _getNextCommandResponse(): Promise<CommandResponse> {
|
||||||
logger('Start read next command response');
|
|
||||||
const response = await this._readFromCommandStream();
|
const response = await this._readFromCommandStream();
|
||||||
const commandResponse = decodeCommandResponse(response);
|
const commandResponse = decodeCommandResponse(response);
|
||||||
logger('next command response: %o', commandResponse);
|
logger('next command response: %o', commandResponse);
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cross-env DEBUG=kboot* electron ./dist/electron-main.js",
|
"start": "cross-env DEBUG=kboot* electron ./dist/electron-main.js",
|
||||||
"electron:spe": "electron ./dist/electron-main.js --spe",
|
"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": "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",
|
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
|
||||||
"lint": "tslint --project tsconfig.json",
|
"lint": "tslint --project tsconfig.json",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import * as commandLineArgs from 'command-line-args';
|
|||||||
import { UhkHidDevice, UhkOperations } from 'uhk-usb';
|
import { UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||||
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
|
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
|
||||||
import { CommandLineArgs, LogRegExps } from 'uhk-common';
|
import { CommandLineArgs, LogRegExps } from 'uhk-common';
|
||||||
import { UhkBlhost } from 'uhk-usb';
|
|
||||||
import { DeviceService } from './services/device.service';
|
import { DeviceService } from './services/device.service';
|
||||||
import { logger } from './services/logger.service';
|
import { logger } from './services/logger.service';
|
||||||
import { AppUpdateService } from './services/app-update.service';
|
import { AppUpdateService } from './services/app-update.service';
|
||||||
@@ -23,8 +22,7 @@ import { loadWindowState, saveWindowState } from './util/window';
|
|||||||
|
|
||||||
const optionDefinitions = [
|
const optionDefinitions = [
|
||||||
{name: 'addons', type: Boolean},
|
{name: 'addons', type: Boolean},
|
||||||
{name: 'spe', type: Boolean}, // simulate privilege escalation error
|
{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
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
|
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
|
||||||
@@ -38,7 +36,6 @@ let win: Electron.BrowserWindow;
|
|||||||
autoUpdater.logger = logger;
|
autoUpdater.logger = logger;
|
||||||
|
|
||||||
let deviceService: DeviceService;
|
let deviceService: DeviceService;
|
||||||
let uhkBlhost: UhkBlhost;
|
|
||||||
let uhkHidDeviceService: UhkHidDevice;
|
let uhkHidDeviceService: UhkHidDevice;
|
||||||
let uhkOperations: UhkOperations;
|
let uhkOperations: UhkOperations;
|
||||||
let appUpdateService: AppUpdateService;
|
let appUpdateService: AppUpdateService;
|
||||||
@@ -103,9 +100,8 @@ function createWindow() {
|
|||||||
|
|
||||||
setMenu(win);
|
setMenu(win);
|
||||||
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
|
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
|
||||||
uhkBlhost = new UhkBlhost(logger, packagesDir);
|
uhkOperations = new UhkOperations(logger, uhkHidDeviceService, packagesDir);
|
||||||
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
|
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir);
|
||||||
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir, options);
|
|
||||||
appUpdateService = new AppUpdateService(logger, win, app);
|
appUpdateService = new AppUpdateService(logger, win, app);
|
||||||
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
|
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
|
||||||
sudoService = new SudoService(logger, options);
|
sudoService = new SudoService(logger, options);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import {
|
import {
|
||||||
CommandLineArgs,
|
|
||||||
ConfigurationReply,
|
ConfigurationReply,
|
||||||
DeviceConnectionState,
|
DeviceConnectionState,
|
||||||
FirmwareUpgradeIpcResponse,
|
FirmwareUpgradeIpcResponse,
|
||||||
@@ -42,8 +41,7 @@ export class DeviceService {
|
|||||||
private win: Electron.BrowserWindow,
|
private win: Electron.BrowserWindow,
|
||||||
private device: UhkHidDevice,
|
private device: UhkHidDevice,
|
||||||
private operations: UhkOperations,
|
private operations: UhkOperations,
|
||||||
private rootDir: string,
|
private rootDir: string) {
|
||||||
private options: CommandLineArgs) {
|
|
||||||
this.startPollUhkDevice();
|
this.startPollUhkDevice();
|
||||||
this.uhkDevicePoller()
|
this.uhkDevicePoller()
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -184,25 +182,15 @@ export class DeviceService {
|
|||||||
const packageJson = await getPackageJsonFromPathAsync(firmwarePathData.packageJsonPath);
|
const packageJson = await getPackageJsonFromPathAsync(firmwarePathData.packageJsonPath);
|
||||||
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
|
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
|
||||||
|
|
||||||
if (this.options.useKboot) {
|
await this.operations.updateRightFirmware(firmwarePathData.rightFirmwarePath);
|
||||||
await this.operations.updateRightFirmwareWithKboot(firmwarePathData.rightFirmwarePath);
|
await this.operations.updateLeftModule(firmwarePathData.leftFirmwarePath);
|
||||||
await this.operations.updateLeftModuleWithKboot(firmwarePathData.leftFirmwarePath);
|
|
||||||
} else {
|
|
||||||
await this.operations.updateRightFirmwareWithBlhost(firmwarePathData.rightFirmwarePath);
|
|
||||||
await this.operations.updateLeftModuleWithBlhost(firmwarePathData.leftFirmwarePath);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const packageJsonPath = path.join(this.rootDir, 'packages/firmware/package.json');
|
const packageJsonPath = path.join(this.rootDir, 'packages/firmware/package.json');
|
||||||
const packageJson = await getPackageJsonFromPathAsync(packageJsonPath);
|
const packageJson = await getPackageJsonFromPathAsync(packageJsonPath);
|
||||||
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
|
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
|
||||||
|
|
||||||
if (this.options.useKboot) {
|
await this.operations.updateRightFirmware();
|
||||||
await this.operations.updateRightFirmwareWithKboot();
|
await this.operations.updateLeftModule();
|
||||||
await this.operations.updateLeftModuleWithKboot();
|
|
||||||
} else {
|
|
||||||
await this.operations.updateRightFirmwareWithBlhost();
|
|
||||||
await this.operations.updateLeftModuleWithBlhost();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response.success = true;
|
response.success = true;
|
||||||
@@ -232,11 +220,7 @@ export class DeviceService {
|
|||||||
try {
|
try {
|
||||||
await this.stopPollUhkDevice();
|
await this.stopPollUhkDevice();
|
||||||
|
|
||||||
if (this.options.useKboot) {
|
await this.operations.updateRightFirmware();
|
||||||
await this.operations.updateRightFirmwareWithKboot();
|
|
||||||
} else {
|
|
||||||
await this.operations.updateRightFirmwareWithBlhost();
|
|
||||||
}
|
|
||||||
|
|
||||||
response.modules = await this.getHardwareModules(false);
|
response.modules = await this.getHardwareModules(false);
|
||||||
response.success = true;
|
response.success = true;
|
||||||
@@ -286,21 +270,19 @@ export class DeviceService {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (this._pollerAllowed) {
|
if (this._pollerAllowed) {
|
||||||
this._uhkDevicePolling = true;
|
|
||||||
try {
|
|
||||||
|
|
||||||
const state = await this.device.getDeviceConnectionStateAsync();
|
this._uhkDevicePolling = true;
|
||||||
if (!isEqual(state, savedState)) {
|
|
||||||
savedState = state;
|
const state = await this.device.getDeviceConnectionStateAsync();
|
||||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
|
if (!isEqual(state, savedState)) {
|
||||||
this.logService.info('[DeviceService] Device connection state changed to:', state);
|
savedState = state;
|
||||||
}
|
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
|
||||||
} catch (err) {
|
this.logService.info('[DeviceService] Device connection state changed to:', state);
|
||||||
this.logService.error('[DeviceService] Device connection state query error', err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._uhkDevicePolling = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._uhkDevicePolling = false;
|
|
||||||
await snooze(250);
|
await snooze(250);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,4 @@ export interface CommandLineArgs {
|
|||||||
* simulate privilege escalation error
|
* simulate privilege escalation error
|
||||||
*/
|
*/
|
||||||
spe?: boolean;
|
spe?: boolean;
|
||||||
/**
|
|
||||||
* If it is true use kboot package instead of blhost for firmware upgrade
|
|
||||||
*/
|
|
||||||
useKboot?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ export enum EnumerationNameToProductId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ModuleSlotToI2cAddress {
|
export enum ModuleSlotToI2cAddress {
|
||||||
leftHalf = '0x10',
|
leftHalf = 0x10,
|
||||||
leftModule = '0x20',
|
leftModule = 0x20,
|
||||||
rightModule = '0x30'
|
rightModule = 0x30
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ModuleSlotToId {
|
export enum ModuleSlotToId {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './constants';
|
export * from './constants';
|
||||||
export * from './uhk-blhost';
|
|
||||||
export * from './uhk-hid-device';
|
export * from './uhk-hid-device';
|
||||||
export * from './uhk-operations';
|
export * from './uhk-operations';
|
||||||
export * from './util';
|
export * from './util';
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -208,7 +208,7 @@ export class UhkHidDevice {
|
|||||||
|
|
||||||
while (new Date().getTime() - startTime.getTime() < 20000) {
|
while (new Date().getTime() - startTime.getTime() < 20000) {
|
||||||
const devs = devices();
|
const devs = devices();
|
||||||
this.logService.debug('[UhkHidDevice] reenumeration devices', devs);
|
this.logService.silly('[UhkHidDevice] reenumeration devices', devs);
|
||||||
|
|
||||||
const inBootloaderMode = devs.some((x: Device) =>
|
const inBootloaderMode = devs.some((x: Device) =>
|
||||||
x.vendorId === Constants.VENDOR_ID &&
|
x.vendorId === Constants.VENDOR_ID &&
|
||||||
@@ -219,7 +219,7 @@ export class UhkHidDevice {
|
|||||||
return;
|
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);
|
await snooze(100);
|
||||||
|
|
||||||
if (!jumped) {
|
if (!jumped) {
|
||||||
@@ -232,7 +232,7 @@ export class UhkHidDevice {
|
|||||||
device.close();
|
device.close();
|
||||||
jumped = true;
|
jumped = true;
|
||||||
} else {
|
} 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) {
|
if (command === KbootCommands.idle) {
|
||||||
transfer = Buffer.from([UsbCommand.SendKbootCommandToModule, command]);
|
transfer = Buffer.from([UsbCommand.SendKbootCommandToModule, command]);
|
||||||
} else {
|
} 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);
|
await retry(async () => await this.write(transfer), maxTry, this.logService);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
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 * as os from 'os';
|
||||||
import { UhkBlhost } from './uhk-blhost';
|
|
||||||
import { UhkHidDevice } from './uhk-hid-device';
|
import { UhkHidDevice } from './uhk-hid-device';
|
||||||
import { readBootloaderFirmwareFromHexFileAsync, snooze, waitForDevice } from './util';
|
import { readBootloaderFirmwareFromHexFileAsync, snooze, waitForDevice } from './util';
|
||||||
import { ConfigBufferId, convertBufferToIntArray, DevicePropertyIds, getTransferBuffers, UsbCommand } from '../index';
|
import { ConfigBufferId, convertBufferToIntArray, DevicePropertyIds, getTransferBuffers, UsbCommand } from '../index';
|
||||||
@@ -21,67 +20,11 @@ import { LoadConfigurationsResult } from './models/load-configurations-result';
|
|||||||
|
|
||||||
export class UhkOperations {
|
export class UhkOperations {
|
||||||
constructor(private logService: LogService,
|
constructor(private logService: LogService,
|
||||||
private blhost: UhkBlhost,
|
|
||||||
private device: UhkHidDevice,
|
private device: UhkHidDevice,
|
||||||
private rootDir: string) {
|
private rootDir: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateRightFirmwareWithBlhost(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');
|
|
||||||
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()) {
|
|
||||||
this.logService.debug(`[UhkOperations] Operating system: ${os.type()} ${os.release()} ${os.arch()}`);
|
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');
|
||||||
|
|
||||||
@@ -115,10 +58,9 @@ export class UhkOperations {
|
|||||||
this.logService.debug('[UhkOperations] Right firmware successfully flashed');
|
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');
|
this.logService.debug('[UhkOperations] Start flashing left module firmware');
|
||||||
|
|
||||||
const i2cAddressOfLeftModule = Number.parseInt(ModuleSlotToI2cAddress.leftHalf, 16);
|
|
||||||
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
|
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
|
||||||
this.device.close();
|
this.device.close();
|
||||||
await snooze(1000);
|
await snooze(1000);
|
||||||
@@ -144,31 +86,28 @@ export class UhkOperations {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
this.logService.debug('[UhkOperations] Try to connect to the LEFT keyboard');
|
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);
|
await kboot.getProperty(Properties.BootloaderVersion);
|
||||||
break;
|
break;
|
||||||
} catch {
|
} catch {
|
||||||
if (tryCount > 100) {
|
if (tryCount > 100) {
|
||||||
throw new Error('Can not connect to the LEFT keyboard');
|
throw new Error('Can not connect to the LEFT keyboard');
|
||||||
}
|
}
|
||||||
await snooze(2000);
|
} finally {
|
||||||
|
kboot.close();
|
||||||
}
|
}
|
||||||
|
await snooze(100);
|
||||||
tryCount++;
|
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');
|
this.logService.debug('[UhkOperations] Flash erase all on LEFT keyboard');
|
||||||
await kboot.configureI2c(i2cAddressOfLeftModule);
|
await kboot.configureI2c(ModuleSlotToI2cAddress.leftHalf);
|
||||||
await kboot.flashEraseAllUnsecure();
|
await kboot.flashEraseAllUnsecure();
|
||||||
|
|
||||||
this.logService.debug('[UhkOperations] Read LEFT firmware from file');
|
this.logService.debug('[UhkOperations] Read LEFT firmware from file');
|
||||||
const configData = fs.readFileSync(firmwarePath);
|
const configData = fs.readFileSync(firmwarePath);
|
||||||
|
|
||||||
this.logService.debug('[UhkOperations] Write memory');
|
this.logService.debug('[UhkOperations] Write memory');
|
||||||
await kboot.configureI2c(i2cAddressOfLeftModule);
|
await kboot.configureI2c(ModuleSlotToI2cAddress.leftHalf);
|
||||||
await kboot.writeMemory({ startAddress: 0, data: configData });
|
await kboot.writeMemory({ startAddress: 0, data: configData });
|
||||||
|
|
||||||
this.logService.debug('[UhkOperations] Reset LEFT keyboard');
|
this.logService.debug('[UhkOperations] Reset LEFT keyboard');
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Device, devices } from 'node-hid';
|
import { Device, devices } from 'node-hid';
|
||||||
import { readFile } from 'fs-extra';
|
import { readFile } from 'fs-extra';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import * as MemoryMap from 'nrf-intel-hex';
|
import MemoryMap from 'nrf-intel-hex';
|
||||||
import { LogService } from 'uhk-common';
|
import { LogService } from 'uhk-common';
|
||||||
|
|
||||||
import { Constants, UsbCommand } from './constants';
|
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>> => {
|
export const readBootloaderFirmwareFromHexFileAsync = async (hexFilePath: string): Promise<Map<any, any>> => {
|
||||||
const fileContent = await readFile(hexFilePath, { encoding: 'utf8' });
|
const fileContent = await readFile(hexFilePath, { encoding: 'utf8' });
|
||||||
const fromHex = MemoryMap.fromHex ? MemoryMap.fromHex : MemoryMap.default.fromHex;
|
const memoryMap = MemoryMap.fromHex(fileContent);
|
||||||
|
|
||||||
const memoryMap = fromHex(fileContent);
|
|
||||||
|
|
||||||
return memoryMap;
|
return memoryMap;
|
||||||
};
|
};
|
||||||
|
|||||||
2037
packages/uhk-web/package-lock.json
generated
2037
packages/uhk-web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,22 +19,22 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/animations": "8.2.6",
|
"@angular/animations": "8.2.3",
|
||||||
"@angular-builders/custom-webpack": "8.2.0",
|
"@angular-builders/custom-webpack": "8.1.0",
|
||||||
"@angular/cli": "8.3.4",
|
"@angular/cli": "8.2.1",
|
||||||
"@angular/common": "8.2.6",
|
"@angular/common": "8.2.3",
|
||||||
"@angular/compiler": "8.2.6",
|
"@angular/compiler": "8.2.3",
|
||||||
"@angular/compiler-cli": "8.2.6",
|
"@angular/compiler-cli": "8.2.3",
|
||||||
"@angular/core": "8.2.6",
|
"@angular/core": "8.2.3",
|
||||||
"@angular-devkit/build-angular": "0.803.4",
|
"@angular-devkit/build-angular": "0.802.1",
|
||||||
"@angular-devkit/build-optimizer": "0.803.4",
|
"@angular-devkit/build-optimizer": "0.802.1",
|
||||||
"@angular-devkit/core": "8.3.4",
|
"@angular-devkit/core": "8.2.1",
|
||||||
"@angular/forms": "8.2.6",
|
"@angular/forms": "8.2.3",
|
||||||
"@angular/language-service": "8.2.6",
|
"@angular/language-service": "8.2.3",
|
||||||
"@angular/platform-browser": "8.2.6",
|
"@angular/platform-browser": "8.2.3",
|
||||||
"@angular/platform-browser-dynamic": "8.2.6",
|
"@angular/platform-browser-dynamic": "8.2.3",
|
||||||
"@angular/router": "8.2.6",
|
"@angular/router": "8.2.3",
|
||||||
"@ngtools/webpack": "8.3.4",
|
"@ngtools/webpack": "8.2.1",
|
||||||
"@ngrx/effects": "8.2.0",
|
"@ngrx/effects": "8.2.0",
|
||||||
"@ngrx/router-store": "8.2.0",
|
"@ngrx/router-store": "8.2.0",
|
||||||
"@ngrx/store": "8.2.0",
|
"@ngrx/store": "8.2.0",
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
} from './store';
|
} from './store';
|
||||||
import { ProgressButtonState } from './store/reducers/progress-button-state';
|
import { ProgressButtonState } from './store/reducers/progress-button-state';
|
||||||
import { UpdateInfo } from './models/update-info';
|
import { UpdateInfo } from './models/update-info';
|
||||||
import { KeyUpAction, KeyDownAction } from './store/actions/app';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'main-app',
|
selector: 'main-app',
|
||||||
@@ -96,13 +95,6 @@ export class MainAppComponent implements OnDestroy {
|
|||||||
this.enableUsbStackTest();
|
this.enableUsbStackTest();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store.dispatch(new KeyDownAction(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
@HostListener('document:keyup', ['$event'])
|
|
||||||
onKeyUp(event: KeyboardEvent) {
|
|
||||||
this.store.dispatch(new KeyUpAction(event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateApp() {
|
updateApp() {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
<li>Right click on a key: Capture key</li>
|
<li>Right click on a key: Capture key</li>
|
||||||
<li>Hold Shift while clicking on a key: Remap on all keymaps</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 while clicking on a key: Remap on all layers</li>
|
||||||
<li>Hold Alt to see macro reference counts in the side menu</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -91,12 +91,6 @@
|
|||||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||||
<a [routerLink]="['/macro', macro.id]"
|
<a [routerLink]="['/macro', macro.id]"
|
||||||
[class.disabled]="state.updatingFirmware">{{macro.name}}</a>
|
[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>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -148,12 +148,6 @@ ul {
|
|||||||
right: 19px;
|
right: 19px;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__macro_count {
|
|
||||||
position: absolute;
|
|
||||||
right: 11px;
|
|
||||||
top: 1px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu--bottom {
|
.menu--bottom {
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
export * from './last-edited-key';
|
export * from './last-edited-key';
|
||||||
export * from './macro-menu-item';
|
|
||||||
export * from './side-menu-page-state';
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export interface MacroMenuItem {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
usageCount: number;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Keymap, Macro } from 'uhk-common';
|
import { Keymap, Macro } from 'uhk-common';
|
||||||
import { MacroMenuItem } from './macro-menu-item';
|
|
||||||
|
|
||||||
export interface SideMenuPageState {
|
export interface SideMenuPageState {
|
||||||
showAddonMenu: boolean;
|
showAddonMenu: boolean;
|
||||||
@@ -7,7 +6,6 @@ export interface SideMenuPageState {
|
|||||||
updatingFirmware: boolean;
|
updatingFirmware: boolean;
|
||||||
deviceName: string;
|
deviceName: string;
|
||||||
keymaps: Keymap[];
|
keymaps: Keymap[];
|
||||||
macros: MacroMenuItem[];
|
macros: Macro[];
|
||||||
restoreUserConfiguration: boolean;
|
restoreUserConfiguration: boolean;
|
||||||
macroUsageCountVisible: boolean;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ export enum ActionTypes {
|
|||||||
SetupPermissionError = '[app] Setup permission error',
|
SetupPermissionError = '[app] Setup permission error',
|
||||||
LoadAppStartInfo = '[app] Load app start info',
|
LoadAppStartInfo = '[app] Load app start info',
|
||||||
StartKeypressCapturing = '[app] Start keypress capturing',
|
StartKeypressCapturing = '[app] Start keypress capturing',
|
||||||
StopKeypressCapturing = '[app] Stop keypress capturing',
|
StopKeypressCapturing = '[app] Stop keypress capturing'
|
||||||
KeyDown = '[app] Key down',
|
|
||||||
KeyUp = '[app] Key up'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AppBootstrappedAction implements Action {
|
export class AppBootstrappedAction implements Action {
|
||||||
@@ -112,18 +110,6 @@ export class StopKeypressCapturingAction implements Action {
|
|||||||
type = ActionTypes.StopKeypressCapturing;
|
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
|
export type Actions
|
||||||
= AppStartedAction
|
= AppStartedAction
|
||||||
| AppBootstrappedAction
|
| AppBootstrappedAction
|
||||||
@@ -141,6 +127,4 @@ export type Actions
|
|||||||
| LoadAppStartInfoAction
|
| LoadAppStartInfoAction
|
||||||
| StartKeypressCapturingAction
|
| StartKeypressCapturingAction
|
||||||
| StopKeypressCapturingAction
|
| StopKeypressCapturingAction
|
||||||
| KeyDownAction
|
|
||||||
| KeyUpAction
|
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ActionReducerMap, createSelector, MetaReducer } from '@ngrx/store';
|
import { ActionReducerMap, createSelector, MetaReducer } from '@ngrx/store';
|
||||||
import { routerReducer, RouterReducerState } from '@ngrx/router-store';
|
import { routerReducer, RouterReducerState } from '@ngrx/router-store';
|
||||||
import { storeFreeze } from 'ngrx-store-freeze';
|
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 fromUserConfig from './reducers/user-configuration';
|
||||||
import * as fromPreset from './reducers/preset';
|
import * as fromPreset from './reducers/preset';
|
||||||
@@ -16,7 +16,6 @@ import { environment } from '../../environments/environment';
|
|||||||
import { RouterStateUrl } from './router-util';
|
import { RouterStateUrl } from './router-util';
|
||||||
import { PrivilagePageSate } from '../models/privilage-page-sate';
|
import { PrivilagePageSate } from '../models/privilage-page-sate';
|
||||||
import { isVersionGte } from '../util';
|
import { isVersionGte } from '../util';
|
||||||
import { SideMenuPageState, MacroMenuItem } from '../models';
|
|
||||||
|
|
||||||
// State interface for the application
|
// State interface for the application
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
@@ -68,7 +67,6 @@ export const deviceConfigurationLoaded = createSelector(appState, fromApp.device
|
|||||||
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
|
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
|
||||||
export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem);
|
export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem);
|
||||||
export const keypressCapturing = createSelector(appState, fromApp.keypressCapturing);
|
export const keypressCapturing = createSelector(appState, fromApp.keypressCapturing);
|
||||||
export const getMacroUsageCountVisible = createSelector(appState, fromApp.macroUsageCountVisible);
|
|
||||||
export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows);
|
export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows);
|
||||||
export const contributors = (state: AppState) => state.contributors;
|
export const contributors = (state: AppState) => state.contributors;
|
||||||
export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindows, notSupportedOs => !notSupportedOs);
|
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(
|
export const getSideMenuPageState = createSelector(
|
||||||
showAddonMenu,
|
showAddonMenu,
|
||||||
runningInElectron,
|
runningInElectron,
|
||||||
updatingFirmware,
|
updatingFirmware,
|
||||||
getUserConfiguration,
|
getUserConfiguration,
|
||||||
getRestoreUserConfiguration,
|
getRestoreUserConfiguration,
|
||||||
getMacroUsageCountVisible,
|
|
||||||
(showAddonMenuValue: boolean,
|
(showAddonMenuValue: boolean,
|
||||||
runningInElectronValue: boolean,
|
runningInElectronValue: boolean,
|
||||||
updatingFirmwareValue: boolean,
|
updatingFirmwareValue: boolean,
|
||||||
userConfiguration: UserConfiguration,
|
userConfiguration: UserConfiguration,
|
||||||
restoreUserConfiguration: boolean,
|
restoreUserConfiguration: boolean) => {
|
||||||
macroUsageCountVisible): SideMenuPageState => {
|
|
||||||
return {
|
return {
|
||||||
showAddonMenu: showAddonMenuValue,
|
showAddonMenu: showAddonMenuValue,
|
||||||
runInElectron: runningInElectronValue,
|
runInElectron: runningInElectronValue,
|
||||||
updatingFirmware: updatingFirmwareValue,
|
updatingFirmware: updatingFirmwareValue,
|
||||||
deviceName: userConfiguration.deviceName,
|
deviceName: userConfiguration.deviceName,
|
||||||
keymaps: userConfiguration.keymaps,
|
keymaps: userConfiguration.keymaps,
|
||||||
macros: getMacroMenuItems(userConfiguration),
|
macros: userConfiguration.macros,
|
||||||
restoreUserConfiguration,
|
restoreUserConfiguration
|
||||||
macroUsageCountVisible
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export interface State {
|
|||||||
platform?: string;
|
platform?: string;
|
||||||
osVersion?: string;
|
osVersion?: string;
|
||||||
keypressCapturing: boolean;
|
keypressCapturing: boolean;
|
||||||
macroUsageCountVisible: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState: State = {
|
export const initialState: State = {
|
||||||
@@ -41,8 +40,7 @@ export const initialState: State = {
|
|||||||
configLoading: true,
|
configLoading: true,
|
||||||
agentVersionInfo: getVersions(),
|
agentVersionInfo: getVersions(),
|
||||||
privilegeWhatWillThisDoClicked: false,
|
privilegeWhatWillThisDoClicked: false,
|
||||||
keypressCapturing: false,
|
keypressCapturing: false
|
||||||
macroUsageCountVisible: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function reducer(
|
export function reducer(
|
||||||
@@ -158,8 +156,7 @@ export function reducer(
|
|||||||
case App.ActionTypes.StartKeypressCapturing:
|
case App.ActionTypes.StartKeypressCapturing:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
keypressCapturing: true,
|
keypressCapturing: true
|
||||||
macroUsageCountVisible: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
case App.ActionTypes.StopKeypressCapturing:
|
case App.ActionTypes.StopKeypressCapturing:
|
||||||
@@ -168,24 +165,6 @@ export function reducer(
|
|||||||
keypressCapturing: false
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -218,4 +197,3 @@ export const runningOnNotSupportedWindows = (state: State): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const keypressCapturing = (state: State): boolean => state.keypressCapturing;
|
export const keypressCapturing = (state: State): boolean => state.keypressCapturing;
|
||||||
export const macroUsageCountVisible = (state: State): boolean => state.macroUsageCountVisible;
|
|
||||||
|
|||||||
@@ -7,18 +7,9 @@ const rootDir = path.join(__dirname, '../../tmp');
|
|||||||
const uhkHidDevice = new UhkHidDevice(logService, {}, rootDir);
|
const uhkHidDevice = new UhkHidDevice(logService, {}, rootDir);
|
||||||
const uhkOperations = new UhkOperations(logService, uhkHidDevice, 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
|
uhkOperations
|
||||||
.updateRightFirmwareWithKboot()
|
.updateRightFirmware()
|
||||||
.then(() => uhkOperations.updateLeftModuleWithKboot())
|
.then(() => uhkOperations.updateLeftModule())
|
||||||
.then(() => console.log('Firmware upgrade finished'))
|
.then(() => console.log('Firmware upgrade finished'))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -8,13 +8,6 @@ const copyOptions = {
|
|||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
promises.push(
|
|
||||||
fse.copy(
|
|
||||||
path.join(__dirname, '../packages/usb/blhost'),
|
|
||||||
path.join(__dirname, '../tmp/packages/blhost'),
|
|
||||||
copyOptions)
|
|
||||||
);
|
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
fse.copy(
|
fse.copy(
|
||||||
path.join(__dirname, '../rules'),
|
path.join(__dirname, '../rules'),
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ if (TEST_BUILD || gitTag) {
|
|||||||
const rootJson = require('../package.json');
|
const rootJson = require('../package.json');
|
||||||
update2ndPackageJson(rootJson);
|
update2ndPackageJson(rootJson);
|
||||||
|
|
||||||
// Add firmware and blhost to extra resources
|
// Add firmware to extra resources
|
||||||
const extractedFirmwareDir = path.join(__dirname, '../tmp/packages');
|
const extractedFirmwareDir = path.join(__dirname, '../tmp/packages');
|
||||||
extraResources.push({from: extractedFirmwareDir, to: 'packages/'});
|
extraResources.push({from: extractedFirmwareDir, to: 'packages/'});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user