33 Commits

Author SHA1 Message Date
László Monda
e294727ac5 Bump Agent version to 1.2.1 and update the changelog. 2018-05-12 11:26:41 +02:00
László Monda
f29d64c803 Rename kbootKommandName to kbootCommandName. 2018-05-09 02:16:38 +02:00
László Monda
0385b0ce29 Simply write that the list of available devices is unchanged instead of always listing the devices. 2018-05-09 01:32:55 +02:00
László Monda
b526274cd7 Upgrade to firmware 8.2.2 2018-05-09 00:38:48 +02:00
László Monda
88c16af4a9 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-05-08 17:17:36 +02:00
László Monda
05ac9a6832 Clarify the content of the tooltop regarding non-US characters. 2018-05-08 17:17:04 +02:00
Kristian Sloth Lauszus
04aa5236c2 The usage page and usage number was changed in 6f0b1adc14 (#630) 2018-05-06 21:38:40 +02:00
László Monda
ec98e4e1c6 Rephrase and add explanations about the macro engine not being ready yet. 2018-05-06 03:24:54 +02:00
László Monda
bb9ece494c Update README.md 2018-05-05 21:55:52 +02:00
László Monda
217e6776ac Add changelog entry that I forgot to add. 2018-05-03 01:38:40 +02:00
László Monda
2286218980 Make long media key macro actions work. (#621)
* Make long media key macro actions work.

* fix: macro key action mapping
2018-05-03 00:28:35 +02:00
Róbert Kiss
3d9c83f9f4 build: check node version before build (#625)
* build: check node version before build

* use package.json engine section
2018-05-01 22:50:28 +02:00
Róbert Kiss
14ed163238 chore: npm run clean delete root node_modules and dist folders (#624) 2018-05-01 22:46:23 +02:00
László Monda
c815de0718 Edit phrasing: "... layer by *tapping* this key" 2018-05-01 03:20:44 +02:00
László Monda
6a46556d9e Make get-right-firmware-version.js display not only firmware version but also device protocol version, module protocol version, user config version and hardware config version. 2018-05-01 02:31:17 +02:00
krokofant
f8f820529f Resolve #553 (#614) 2018-04-29 21:32:33 +02:00
László Monda
cac11155e7 Update firmware version to 8.2.0 2018-04-20 10:04:21 +02:00
László Monda
d20870f11e Update firmware version and default mouse speed. 2018-04-20 09:49:27 +02:00
László Monda
10ceb6c79d Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-04-20 09:19:58 +02:00
Róbert Kiss
b38b6fa294 build: fix nouislider conflict in webpack conflict (#611)
* build: fix nouislider conflict in webpack conflict and upgrade nouislider

* use package import instead of sub file
2018-04-20 09:19:10 +02:00
László Monda
94c1d35429 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-04-19 22:31:25 +02:00
László Monda
e33cef4e89 Update README. Fix #609 2018-04-13 19:32:20 +02:00
Róbert Kiss
9b815ed9c1 chore: upgrade angular to 5.3.9 and typescript to 2.6.2 (#605)
* chore: upgrade angular to 5.3.9 and typescript to 2.6.2

* fix electron renderer build

* fix webpack config

* format webpack.config

* fix renderer build
2018-04-10 19:52:58 +02:00
László Monda
1d4bb6113c Bump version to 1.1.5 in package.json and update the changelog. 2018-04-10 11:42:34 +02:00
Róbert Kiss
136120b831 feat: not allow run multiple Agent (#603) 2018-04-09 21:09:09 +02:00
Róbert Kiss
cd299c06d6 feat: not allow run multiple Agent (#604) 2018-04-09 20:48:22 +02:00
László Monda
e152a36ad7 Add agent-app-icon.png which I forgot to add. 2018-04-09 14:16:09 +02:00
László Monda
e90544db33 Bump Agent version to 1.1.4 in package.json and update the changelog. 2018-04-09 13:59:38 +02:00
László Monda
7ceca202b4 Reposition the ISO key in the scancode list. 2018-04-09 12:48:44 +02:00
László Monda
ddc65aa54b Replace application icon with a diagonal gradient based icon that should look better on the desktop. 2018-04-09 12:20:46 +02:00
Róbert Kiss
13ec617d58 Make saving the configuration more robust (#594)
* feat: Make saving the configuration more robust

* parse backup user config before return

* fix some bug

* Add write-userconfig.js and invalid-config.bin

* throw exception if failed user config parsing

* Merge branch 'master' into feat-467-make-save-more-robust

* hide keymaps and macros if agent in restore mode

* fix Device name settings
2018-04-09 10:11:26 +02:00
Róbert Kiss
00c5b69129 fix: display agent icon when user use ALT + TAB (#600) 2018-04-07 23:17:50 +02:00
Róbert Kiss
6ccf005750 feat: Handle privilege escalation gracefully even without PolicyKit (#599)
* feat: Handle privilege escalation gracefully even without PolicyKit

* build: upgrade tslint => 5.9.1

* build: add uhk-agent/package-lock.json

* feat: add error animation

* fix: display agent icon when user use ALT + TAB
2018-04-07 23:09:47 +02:00
90 changed files with 7657 additions and 5161 deletions

View File

@@ -6,6 +6,38 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
## [1.2.1] - 2018-05-12
Firmware: 8.2.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.2)] | Device Protocol: 4.3.0| User Config: 4.0.**1** | Hardware Config: 1.0.0
- Match for the new USB usage page and usage number. This is critical for UHKs flashed with firmware >=8.2.2 to be recognized by Agent on OSX.
- Make the config serializer handle long media macro actions. `USERCONFIG:PATCH`
- Add note on the macro page explaining that the macro engine of the firmware is not ready yet.
- Add an example to the scancode tooltip to better explain users how to invoke non-US characters.
## [1.2.0] - 2018-04-20
Firmware: 8.**2.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.0)] | Device Protocol: 4.**3.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
- Tweak the default mouse speed. This was necessary because the last firmware version adjusted speed multipliers. The mouse speed can be reset via the "Reset speeds to default" button of the "Mouse speed" page.
- Make the newly added switch-keymap.js script utilize the new UsbCommandId_SwitchKeymap, allowing for programmatic keymap switching. `DEVICEPROTOCOL:MINOR`
## [1.1.5] - 2018-04-10
Firmware: 8.1.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Don't allow to run multiple instances of Agent at the same time, but rather focus the already existing Agent window.
## [1.1.4] - 2018-04-09
Firmware: 8.1.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Handle privilege escalation gracefully on Linux even without PolicyKit.
- Fix application icon path.
- Replace application icon with a diagonal gradient based icon that should look better on desktop.
- Make saving the configuration more robust, and add a configuration recovery screen.
- Reposition the ISO key in the scancode list.
## [1.1.3] - 2018-04-06
Firmware: 8.1.**5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
@@ -40,7 +72,7 @@ Firmware: 8.1.**2** [[release](https://github.com/UltimateHackingKeyboard/firmwa
## [1.1.0] - 2018-01-15
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.**2.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
- Only accept device, keymap, and macro names upon editing if their trimmed length is non-zero.
- Add diagnostics USB scripts, most notably /packages/usb/{get-i2c-health,set-i2c-baud-rate}.js, some utilizing new device protocol commands and properties. `DEVICEPROTOCOL:MINOR`

View File

@@ -5,17 +5,8 @@
Agent is the configuration application of the [Ultimate Hacking Keyboard](https://ultimatehackingkeyboard.com/).
[Give it a whirl!](http://ultimatehackingkeyboard.github.io/agent/)
## Two builds to rule them all
It's worth mentioning that Agent has two builds.
The **electron build** is the desktop application which is meant to be used if you have an actual UHK at hand. It starts with an opening screen which detects your UHK. You cannot get past this screen without connecting a UHK via USB.
The **web build** is meant to be used for demonstration purposes, so people who don't yet own a UHK can get a feel of Agent and its capabilities in their browser. Eventually, WebUSB support will be added to the web build, making it able to communicate with the UHK. Given the sandboxed nature of browsers, the web build will always lack features that the electron build offers, so this won't make the electron build obsolete.
The two builds share code as much as possible.
* Try out the [web build of Agent](http://ultimatehackingkeyboard.github.io/agent/) in your browser. This is meant to be used for demonstration purposes.
* Download the [desktop build of Agent](https://github.com/UltimateHackingKeyboard/agent/releases) from our releases page. Use this if you have an actual UHK at hand, or else you won't get past the opening screen!
## Building the electron application
@@ -33,9 +24,9 @@ For everyone else, use the appropriate package manager for your OS.
```
git clone git@github.com:UltimateHackingKeyboard/agent.git
cd agent
npm install # to install Node dependencies
npm run build:electron # to build the agent
npm run electron # to run the newly built agent
npm install
npm run build
npm run electron
```
At this point, Agent should be running on your machine.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

2672
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,10 +3,10 @@
"private": true,
"author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js",
"version": "1.1.3",
"firmwareVersion": "8.1.5",
"deviceProtocolVersion": "4.2.0",
"userConfigVersion": "4.0.0",
"version": "1.2.1",
"firmwareVersion": "8.2.2",
"deviceProtocolVersion": "4.3.0",
"userConfigVersion": "4.0.1",
"hardwareConfigVersion": "1.0.0",
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
"repository": {
@@ -21,8 +21,9 @@
"devDependencies": {
"@types/electron-devtools-installer": "2.0.2",
"@types/electron-settings": "3.0.0",
"@types/fs-extra": "4.0.5",
"@types/fs-extra": "5.0.1",
"@types/jasmine": "2.6.0",
"@types/jquery": "3.3.1",
"@types/jsonfile": "4.0.1",
"@types/node": "8.0.53",
"@types/node-hid": "0.5.2",
@@ -30,8 +31,9 @@
"@types/usb": "1.1.3",
"autoprefixer": "6.5.3",
"buffer": "5.0.6",
"copyfiles": "^2.0.0",
"check-node-version": "^3.2.0",
"copy-webpack-plugin": "4.0.1",
"copyfiles": "^2.0.0",
"core-js": "2.4.1",
"cross-env": "5.0.5",
"decompress": "4.2.0",
@@ -47,10 +49,12 @@
"electron-updater": "2.21.4",
"exports-loader": "0.6.3",
"file-loader": "0.10.0",
"fs-extra": "4.0.2",
"fs-extra": "5.0.0",
"gh-pages": "1.1.0",
"jsonfile": "4.0.0",
"lerna": "2.9.0",
"mkdirp": "0.5.1",
"node-hid": "0.5.7",
"npm-run-all": "4.0.2",
"pre-commit": "1.2.2",
"request": "2.83.0",
@@ -60,9 +64,9 @@
"svg-sprite": "1.3.7",
"ts-loader": "2.3.1",
"ts-node": "3.0.4",
"tslint": "5.5.0",
"typescript": "2.5.2",
"webpack": "2.4.1"
"tslint": "5.9.1",
"typescript": "2.6.2",
"webpack": "3.10.0"
},
"pre-commit": [
"precommit-msg"
@@ -76,12 +80,13 @@
"test:uhk-web": "lerna exec --scope uhk-web npm test",
"lint": "run-s -scn lint:ts lint:style",
"lint:ts": "run-p -sn lint:ts:electron-main lint:ts:electron-renderer lint:ts:web lint:ts:test-serializer lint:ts:uhk-usb",
"lint:ts:electron-main": "tslint --type-check --project ./packages/uhk-agent/tsconfig.json",
"lint:ts:electron-renderer": "tslint --type-check --project ./packages/uhk-web/src/tsconfig.renderer.json",
"lint:ts:web": "tslint --type-check --project ./packages/uhk-web/src/tsconfig.app.json",
"lint:ts:test-serializer": "tslint --type-check --project ./packages/test-serializer/tsconfig.json",
"lint:ts:uhk-usb": "tslint --type-check --project ./packages/uhk-usb/tsconfig.json",
"lint:ts:electron-main": "tslint --project ./packages/uhk-agent/tsconfig.json",
"lint:ts:electron-renderer": "tslint --project ./packages/uhk-web/src/tsconfig.renderer.json",
"lint:ts:web": "tslint --project ./packages/uhk-web/src/tsconfig.app.json",
"lint:ts:test-serializer": "tslint --project ./packages/test-serializer/tsconfig.json",
"lint:ts:uhk-usb": "tslint --project ./packages/uhk-usb/tsconfig.json",
"lint:style": "stylelint \"packages/uhk-agent/src/**/*.scss\" \"packages/uhk-web/src/**/*.scss\" --syntax scss",
"prebuild": "check-node-version --package",
"build": "run-s build:common build:usb build:web build:electron",
"build:web": "lerna exec --scope uhk-web npm run build",
"build:electron": "cross-env AOT_BUILD=true run-s -sn build:electron:renderer build:electron:main",
@@ -92,11 +97,14 @@
"server:web": "lerna exec --scope uhk-web npm start",
"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",
"standard-version": "standard-version",
"pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites",
"release": "node ./scripts/release.js",
"clean": "lerna exec rimraf ./node_modules ./dist"
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist",
"predeploy-gh-pages": "run-s build:web",
"deploy-gh-pages": "gh-pages -d packages/uhk-web/dist"
},
"dependencies": {}
}

763
packages/uhk-agent/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@
"command-line-args": "4.0.7",
"decompress": "4.2.0",
"decompress-bzip2": "4.0.0",
"node-hid": "0.5.4",
"node-hid": "0.5.7",
"sudo-prompt": "7.0.0",
"tmp": "0.0.33",
"uhk-common": "^1.0.0",
@@ -30,6 +30,7 @@
},
"scripts": {
"start": "electron ./dist/electron-main.js",
"electron:spe": "electron ./dist/electron-main.js --spe",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost",
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
"install:build-deps": "cd ./dist && npm i",

View File

@@ -2,7 +2,7 @@
/// <reference path="./custom_types/command-line-args.d.ts"/>
import './polyfills';
import { app, BrowserWindow, ipcMain } from 'electron';
import { app, BrowserWindow } from 'electron';
import { autoUpdater } from 'electron-updater';
import * as path from 'path';
@@ -10,7 +10,7 @@ import * as url from 'url';
import * as commandLineArgs from 'command-line-args';
import { UhkHidDevice, UhkOperations } from 'uhk-usb';
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
import { LogRegExps } from 'uhk-common';
import { CommandLineArgs, LogRegExps } from 'uhk-common';
import { DeviceService } from './services/device.service';
import { logger } from './services/logger.service';
import { AppUpdateService } from './services/app-update.service';
@@ -18,13 +18,13 @@ import { AppService } from './services/app.service';
import { SudoService } from './services/sudo.service';
import { UhkBlhost } from '../../uhk-usb/src';
import * as isDev from 'electron-is-dev';
import { CommandLineInputs } from './models/command-line-inputs';
const optionDefinitions = [
{name: 'addons', type: Boolean}
{name: 'addons', type: Boolean},
{name: 'spe', type: Boolean} // simulate privilege escalation error
];
const options: CommandLineInputs = commandLineArgs(optionDefinitions);
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
// import './dev-extension';
// require('electron-debug')({ showDevTools: true, enabled: true });
@@ -60,7 +60,25 @@ if (console.debug) {
};
}
const isSecondInstance = app.makeSingleInstance(function (commandLine, workingDirectory) {
// Someone tried to run a second instance, we should focus our window.
if (win) {
if (win.isMinimized()) {
win.restore();
}
win.focus();
}
});
if (isSecondInstance) {
app.quit();
}
function createWindow() {
if (isSecondInstance) {
return;
}
logger.info('[Electron Main] Create new window.');
let packagesDir;
if (isDev) {
@@ -79,17 +97,17 @@ function createWindow() {
webPreferences: {
nodeIntegration: true
},
icon: 'assets/images/agent-icon.png'
icon: path.join(__dirname, 'renderer/assets/images/agent-app-icon.png')
});
win.setMenuBarVisibility(false);
win.maximize();
uhkHidDeviceService = new UhkHidDevice(logger);
uhkHidDeviceService = new UhkHidDevice(logger, options);
uhkBlhost = new UhkBlhost(logger, packagesDir);
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations);
appUpdateService = new AppUpdateService(logger, win, app);
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
sudoService = new SudoService(logger);
sudoService = new SudoService(logger, options);
// and load the index.html of the app.
win.loadURL(url.format({
@@ -133,13 +151,13 @@ app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', () => {
app.quit();
});
app.on('will-quit', () => {
if (appUpdateService) {
appUpdateService.saveFirtsRun();
}
app.exit();
});
app.on('will-quit', () => {
});
app.on('activate', () => {

View File

@@ -1,3 +1,10 @@
export interface CommandLineInputs {
/**
* addons menu visible or not
*/
addons?: boolean;
/**
* simulate privilege escalation error
*/
spe?: boolean;
}

View File

@@ -1,5 +1,15 @@
import { ipcMain } from 'electron';
import { ConfigurationReply, DeviceConnectionState, HardwareModules, IpcEvents, IpcResponse, LogService } from 'uhk-common';
import {
ConfigurationReply,
DeviceConnectionState,
getHardwareConfigFromDeviceResponse,
HardwareModules,
IpcEvents,
IpcResponse,
LogService,
mapObjectToUserConfigBinaryBuffer,
SaveUserConfigurationData
} from 'uhk-common';
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
@@ -14,6 +24,7 @@ import 'rxjs/add/operator/distinctUntilChanged';
import { saveTmpFirmware } from '../util/save-extract-firmware';
import { TmpFirmware } from '../models/tmp-firmware';
import { QueueManager } from './queue-manager';
import { backupUserConfiguration, getBackupUserConfigurationContent } from '../util/backup-user-confoguration';
/**
* IpcMain pair of the UHK Communication
@@ -78,10 +89,14 @@ export class DeviceService {
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
};
const hardwareConfig = getHardwareConfigFromDeviceResponse(result.hardwareConfiguration);
const uniqueId = hardwareConfig.uniqueId;
response = {
success: true,
...result,
modules
modules,
backupConfiguration: await getBackupUserConfigurationContent(this.logService, uniqueId)
};
} catch (error) {
response = {
@@ -162,10 +177,13 @@ export class DeviceService {
private async saveUserConfiguration(event: Electron.Event, args: Array<string>): Promise<void> {
const response = new IpcResponse();
const json = args[0];
const data: SaveUserConfigurationData = JSON.parse(args[0]);
try {
await this.operations.saveUserConfiguration(json);
await backupUserConfiguration(data);
const buffer = mapObjectToUserConfigBinaryBuffer(data.configuration);
await this.operations.saveUserConfiguration(buffer);
response.success = true;
}

View File

@@ -5,12 +5,13 @@ import * as sudo from 'sudo-prompt';
import { dirSync } from 'tmp';
import { emptyDir, copy } from 'fs-extra';
import { IpcEvents, LogService, IpcResponse } from 'uhk-common';
import { CommandLineArgs, IpcEvents, LogService, IpcResponse } from 'uhk-common';
export class SudoService {
private rootDir: string;
constructor(private logService: LogService) {
constructor(private logService: LogService,
private options: CommandLineArgs) {
if (isDev) {
this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '../../../../');
} else {
@@ -21,6 +22,19 @@ export class SudoService {
}
private async setPrivilege(event: Electron.Event) {
if (this.options.spe) {
const error = new Error('No polkit authentication agent found.');
this.logService.error('[SudoService] Simulate privilege escalation error ', error);
const response = new IpcResponse();
response.success = false;
response.error = {message: error.message};
event.sender.send(IpcEvents.device.setPrivilegeOnLinuxReply, response);
return;
}
switch (process.platform) {
case 'linux':
await this.setPrivilegeOnLinux(event);
@@ -55,7 +69,7 @@ export class SudoService {
if (error) {
this.logService.error('[SudoService] Error when set privilege: ', error);
response.success = false;
response.error = error;
response.error = {message: error.message};
} else {
response.success = true;
}

View File

@@ -0,0 +1,32 @@
import { app } from 'electron';
import { LogService, UserConfiguration, SaveUserConfigurationData } from 'uhk-common';
import * as path from 'path';
import * as fs from 'fs-extra';
export const getBackupUserConfigurationPath = (uniqueId: number): string => {
const appDataDir = app.getPath('userData');
return path.join(appDataDir, `${uniqueId}.json`);
};
export const backupUserConfiguration = (data: SaveUserConfigurationData): Promise<void> => {
const backupFilePath = getBackupUserConfigurationPath(data.uniqueId);
return fs.writeJSON(backupFilePath, data.configuration, {spaces: 2});
};
export const getBackupUserConfigurationContent = async (logService: LogService, uniqueId: number): Promise<UserConfiguration> => {
try {
const backupFilePath = getBackupUserConfigurationPath(uniqueId);
if (await fs.pathExists(backupFilePath)) {
const json = await fs.readJSON(backupFilePath);
new UserConfiguration().fromJsonObject(json);
return json;
}
return null;
} catch (error) {
logService.error('Can not load backup user configuration for device', {uniqueId, error});
}
};

View File

@@ -18,11 +18,11 @@
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "1.9.0"
"color-convert": "1.9.1"
}
},
"arrify": {
@@ -36,9 +36,9 @@
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
@@ -55,13 +55,13 @@
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
},
"chalk": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
"integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"requires": {
"ansi-styles": "3.2.0",
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
"supports-color": "4.4.0"
"supports-color": "5.3.0"
}
},
"cliui": {
@@ -100,9 +100,9 @@
}
},
"color-convert": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
"integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
"requires": {
"color-name": "1.1.3"
}
@@ -122,15 +122,15 @@
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"requires": {
"lru-cache": "4.1.1",
"lru-cache": "4.1.2",
"shebang-command": "1.2.0",
"which": "1.3.0"
},
"dependencies": {
"lru-cache": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
"integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
"integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
"requires": {
"pseudomap": "1.0.2",
"yallist": "2.1.2"
@@ -144,9 +144,9 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"diff": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz",
"integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA=="
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
},
"error-ex": {
"version": "1.3.1",
@@ -271,9 +271,9 @@
"integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto="
},
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"homedir-polyfill": {
"version": "1.0.1",
@@ -284,9 +284,9 @@
}
},
"hosted-git-info": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
"integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg=="
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
"integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw=="
},
"inflight": {
"version": "1.0.6",
@@ -382,7 +382,7 @@
"requires": {
"jasmine": "2.8.0",
"ts-node": "3.3.0",
"typescript": "2.5.3",
"typescript": "2.8.1",
"yargs": "8.0.2"
}
},
@@ -432,29 +432,29 @@
"integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
},
"make-error": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz",
"integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y="
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz",
"integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g=="
},
"mem": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
"integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
"requires": {
"mimic-fn": "1.1.0"
"mimic-fn": "1.2.0"
}
},
"mimic-fn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
"integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg="
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "1.1.8"
"brace-expansion": "1.1.11"
}
},
"minimist": {
@@ -472,10 +472,10 @@
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
"requires": {
"hosted-git-info": "2.5.0",
"hosted-git-info": "2.6.0",
"is-builtin-module": "1.0.0",
"semver": "5.4.1",
"validate-npm-package-license": "3.0.1"
"semver": "5.5.0",
"validate-npm-package-license": "3.0.3"
}
},
"npm-run-path": {
@@ -1942,18 +1942,26 @@
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-limit": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
"integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw="
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
"integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
"requires": {
"p-try": "1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"requires": {
"p-limit": "1.1.0"
"p-limit": "1.2.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
@@ -2030,9 +2038,9 @@
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
},
"semver": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
},
"set-blocking": {
"version": "2.0.0",
@@ -2076,22 +2084,32 @@
}
},
"spdx-correct": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
"integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
"integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
"requires": {
"spdx-license-ids": "1.2.2"
"spdx-expression-parse": "3.0.0",
"spdx-license-ids": "3.0.0"
}
},
"spdx-exceptions": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
"integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
},
"spdx-expression-parse": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
"integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"requires": {
"spdx-exceptions": "2.1.0",
"spdx-license-ids": "3.0.0"
}
},
"spdx-license-ids": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
"integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
"integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA=="
},
"string-width": {
"version": "2.1.1",
@@ -2146,11 +2164,11 @@
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"supports-color": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
"integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
"integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
"requires": {
"has-flag": "2.0.0"
"has-flag": "3.0.0"
}
},
"ts-node": {
@@ -2159,14 +2177,14 @@
"integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=",
"requires": {
"arrify": "1.0.1",
"chalk": "2.1.0",
"diff": "3.4.0",
"make-error": "1.3.0",
"chalk": "2.3.2",
"diff": "3.5.0",
"make-error": "1.3.4",
"minimist": "1.2.0",
"mkdirp": "0.5.1",
"source-map-support": "0.4.18",
"tsconfig": "6.0.0",
"v8flags": "3.0.1",
"v8flags": "3.0.2",
"yn": "2.0.0"
},
"dependencies": {
@@ -2202,9 +2220,9 @@
}
},
"typescript": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz",
"integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w=="
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz",
"integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg=="
},
"underscore": {
"version": "1.6.0",
@@ -2212,20 +2230,20 @@
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
},
"v8flags": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz",
"integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.2.tgz",
"integrity": "sha512-6sgSKoFw1UpUPd3cFdF7QGnrH6tDeBgW1F3v9gy8gLY0mlbiBXq8soy8aQpY6xeeCjH5K+JvC62Acp7gtl7wWA==",
"requires": {
"homedir-polyfill": "1.0.1"
}
},
"validate-npm-package-license": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
"integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
"integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
"requires": {
"spdx-correct": "1.0.2",
"spdx-expression-parse": "1.0.4"
"spdx-correct": "3.0.0",
"spdx-expression-parse": "3.0.0"
}
},
"walkdir": {

View File

@@ -3,7 +3,8 @@
"private": true,
"version": "1.0.0",
"description": "Common Library contains the common code for uhk-agent (electron-main) and web (electron-renderer) modules",
"main": "dist/index.js",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"author": "Ultimate Gadget Laboratories",
"repository": {
"type": "git",

View File

@@ -13,7 +13,7 @@ export enum KeystrokeActionFlag {
const KEYSTROKE_ACTION_FLAG_LENGTH = 3;
interface JsonObjectKeystrokeAction {
export interface JsonObjectKeystrokeAction {
keyActionType: string;
scancode?: number;
modifierMask?: number;

View File

@@ -1,10 +1,10 @@
import { assertEnum, assertUInt8 } from '../../assert';
import { assertEnum, assertUInt8, assertUInt16 } from '../../assert';
import { UhkBuffer } from '../../uhk-buffer';
import { KeyModifiers } from '../key-modifiers';
import { MacroAction, MacroActionId, MacroKeySubAction, macroActionType } from './macro-action';
import { KeystrokeType } from '../key-action';
interface JsObjectKeyMacroAction {
export interface JsObjectKeyMacroAction {
macroActionType: string;
action: string;
type?: string;
@@ -20,12 +20,24 @@ export class KeyMacroAction extends MacroAction {
@assertEnum(KeystrokeType)
type: KeystrokeType;
@assertUInt8
scancode: number;
@assertUInt8
modifierMask: number;
@assertUInt16
private _scancode: number;
set scancode(scancode: number) {
this._scancode = scancode;
if (this.type !== KeystrokeType.shortMedia && this.type !== KeystrokeType.longMedia) {
return;
}
this.type = scancode < 256 ? KeystrokeType.shortMedia : KeystrokeType.longMedia;
}
get scancode() {
return this._scancode;
}
constructor(other?: KeyMacroAction) {
super();
if (!other) {
@@ -33,7 +45,7 @@ export class KeyMacroAction extends MacroAction {
}
this.action = other.action;
this.type = other.type;
this.scancode = other.scancode;
this._scancode = other._scancode;
this.modifierMask = other.modifierMask;
}
@@ -45,7 +57,7 @@ export class KeyMacroAction extends MacroAction {
} else {
this.type = KeystrokeType[jsObject.type];
}
this.scancode = jsObject.scancode;
this._scancode = jsObject.scancode;
this.modifierMask = jsObject.modifierMask;
return this;
}
@@ -58,7 +70,7 @@ export class KeyMacroAction extends MacroAction {
this.type = keyMacroType & 0b11;
keyMacroType >>= 2;
if (keyMacroType & 0b10) {
this.scancode = buffer.readUInt8();
this._scancode = this.type === KeystrokeType.longMedia ? buffer.readUInt16() : buffer.readUInt8();
}
if (keyMacroType & 0b01) {
this.modifierMask = buffer.readUInt8();
@@ -78,7 +90,7 @@ export class KeyMacroAction extends MacroAction {
} else {
jsObject.type = KeystrokeType[this.type];
}
jsObject.scancode = this.scancode;
jsObject.scancode = this._scancode;
}
if (this.hasModifiers()) {
@@ -98,15 +110,19 @@ export class KeyMacroAction extends MacroAction {
buffer.writeUInt8(keyMacroType);
if (this.hasScancode()) {
if (this.type === KeystrokeType.longMedia) {
buffer.writeUInt16(this.scancode);
} else {
buffer.writeUInt8(this.scancode);
}
}
if (this.hasModifiers()) {
buffer.writeUInt8(this.modifierMask);
}
}
toString(): string {
return `<KeyMacroAction action="${this.action}" scancode="${this.scancode}" modifierMask="${this.modifierMask}">`;
return `<KeyMacroAction action="${this.action}" scancode="${this._scancode}" modifierMask="${this.modifierMask}">`;
}
isModifierActive(modifier: KeyModifiers): boolean {
@@ -114,7 +130,7 @@ export class KeyMacroAction extends MacroAction {
}
hasScancode(): boolean {
return !!this.scancode;
return !!this._scancode;
}
hasModifiers(): boolean {

View File

@@ -8,7 +8,7 @@ export enum MouseButtons {
Right = 1 << 2
}
interface JsObjectMouseButtonMacroAction {
export interface JsObjectMouseButtonMacroAction {
macroActionType: string;
action: string;
mouseButtonsMask?: number;

View File

@@ -105,6 +105,10 @@
{
"id": "29",
"text": "Z"
},
{
"id": "100",
"text": "| ISO"
}
]
},
@@ -314,10 +318,6 @@
"id": "69",
"text": "F12"
},
{
"id": "100",
"text": "| ISO"
},
{
"id": "104",
"text": "F13"

View File

@@ -1,3 +1,10 @@
export interface CommandLineArgs {
addons: boolean;
/**
* addons menu visible or not
*/
addons?: boolean;
/**
* simulate privilege escalation error
*/
spe?: boolean;
}

View File

@@ -1,4 +1,5 @@
import { HardwareModules } from './hardware-modules';
import { UserConfiguration } from '../config-serializer/config-items';
export interface ConfigurationReply {
success: boolean;
@@ -6,4 +7,5 @@ export interface ConfigurationReply {
hardwareConfiguration?: string;
modules?: HardwareModules;
error?: string;
backupConfiguration?: UserConfiguration;
}

View File

@@ -7,3 +7,4 @@ export * from './version-information';
export * from './device-connection-state';
export * from './hardware-modules';
export * from './hardware-module-info';
export * from './save-user-configuration-data';

View File

@@ -0,0 +1,4 @@
export interface SaveUserConfigurationData {
uniqueId: number;
configuration: string;
}

View File

@@ -0,0 +1,33 @@
import { HardwareConfiguration, UhkBuffer, UserConfiguration } from '../config-serializer';
export const getHardwareConfigFromDeviceResponse = (json: string): HardwareConfiguration => {
const data = JSON.parse(json);
const hardwareConfig = new HardwareConfiguration();
hardwareConfig.fromBinary(UhkBuffer.fromArray(data));
if (hardwareConfig.uniqueId > 0) {
return hardwareConfig;
}
return null;
};
export const getUserConfigFromDeviceResponse = (json: string): UserConfiguration => {
const data = JSON.parse(json);
const userConfig = new UserConfiguration();
userConfig.fromBinary(UhkBuffer.fromArray(data));
if (userConfig.userConfigMajorVersion > 0) {
return userConfig;
}
throw Error('Invalid user configuration');
};
export const mapObjectToUserConfigBinaryBuffer = (obj: any): Buffer => {
const configuration = new UserConfiguration();
configuration.fromJsonObject(obj);
const buffer = new UhkBuffer();
configuration.toBinary(buffer);
return buffer.getBufferContent();
};

View File

@@ -1,6 +1,7 @@
export { IpcEvents } from './ipcEvents';
export * from './log';
export * from './constants';
export * from './helpers';
// Source: http://stackoverflow.com/questions/13720256/javascript-regex-camelcase-to-sentence
export function camelCaseToSentence(camelCasedText: string): string {

View File

@@ -1,4 +1,4 @@
class App {
export class App {
public static readonly appStarted = 'app-started';
public static readonly getAppStartInfo = 'app-get-start-info';
public static readonly getAppStartInfoReply = 'app-get-start-info-reply';
@@ -6,7 +6,7 @@ class App {
public static readonly openUrl = 'open-url';
}
class AutoUpdate {
export class AutoUpdate {
public static readonly checkingForUpdate = 'checking-for-update';
public static readonly updateAvailable = 'update-available';
public static readonly updateNotAvailable = 'update-not-available';
@@ -18,7 +18,7 @@ class AutoUpdate {
public static readonly checkForUpdateNotAvailable = 'check-for-update-not-available';
}
class Device {
export class Device {
public static readonly setPrivilegeOnLinux = 'set-privilege-on-linux';
public static readonly setPrivilegeOnLinuxReply = 'set-privilege-on-linux-reply';
public static readonly deviceConnectionStateChanged = 'device-connection-state-changed';

View File

@@ -3,7 +3,7 @@
"compilerOptions": {
"sourceMap": true,
"outDir": "./dist",
"declaration": false,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,

View File

@@ -1,11 +1,14 @@
{
"requires": true,
"name": "uhk-usb",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/node": {
"version": "8.0.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ=="
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==",
"dev": true
},
"ansi-regex": {
"version": "2.1.1",
@@ -23,7 +26,7 @@
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"requires": {
"delegates": "1.0.0",
"readable-stream": "2.3.3"
"readable-stream": "2.3.6"
}
},
"bindings": {
@@ -32,11 +35,12 @@
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw=="
},
"bl": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
"integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
"readable-stream": "2.3.3"
"readable-stream": "2.3.6",
"safe-buffer": "5.1.1"
}
},
"chownr": {
@@ -59,6 +63,14 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"decompress-response": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
"integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
"requires": {
"mimic-response": "1.0.0"
}
},
"deep-extend": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
@@ -69,10 +81,15 @@
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"end-of-stream": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz",
"integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"requires": {
"once": "1.4.0"
}
@@ -113,9 +130,9 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
"integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4="
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
@@ -130,6 +147,16 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"lodash-es": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.10.tgz",
"integrity": "sha512-iesFYPmxYYGTcmQK0sL8bX3TGHyM6b2qREaB4kamHfQyfPJP0xgoGxp19nsH16nsfquLdiyKyX3mQkfiSGV8Rg=="
},
"mimic-response": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz",
"integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4="
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
@@ -151,14 +178,17 @@
}
},
"nan": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
"integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY="
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
},
"node-abi": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.1.1.tgz",
"integrity": "sha512-6oxV13poCOv7TfGvhsSz6XZWpXeKkdGVh72++cs33OfMh3KAX8lN84dCvmqSETyDXAFcUHtV7eJrgFBoOqZbNQ=="
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.3.0.tgz",
"integrity": "sha512-zwm6vU3SsVgw3e9fu48JBaRBCJGIvAgysDsqtf5+vEexFE71bEOtaMWb5zr/zODZNzTPtQlqUUpC79k68Hspow==",
"requires": {
"semver": "5.5.0"
}
},
"node-hid": {
"version": "0.5.7",
@@ -166,8 +196,8 @@
"integrity": "sha512-dwwpOetL2+MGYgivbO22ML+45ieCGbueWv1rYxRgBoEc2QMp6UF6ZucEkYts1IA3YPWJNkmpGh6dqQ85n19szw==",
"requires": {
"bindings": "1.3.0",
"nan": "2.7.0",
"prebuild-install": "2.3.0"
"nan": "2.10.0",
"prebuild-install": "2.5.1"
}
},
"noop-logger": {
@@ -210,62 +240,63 @@
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"prebuild-install": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.3.0.tgz",
"integrity": "sha512-gzjq2oHB8oMbzJSsSh9MQ64zrXZGt092/uT4TLZlz2qnrPxpWqp4vYB7LZrDxnlxf5RfbCjkgDI/z0EIVuYzAw==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.1.tgz",
"integrity": "sha512-3DX9L6pzwc1m1ksMkW3Ky2WLgPQUBiySOfXVl3WZyAeJSyJb4wtoH9OmeRGcubAWsMlLiL8BTHbwfm/jPQE9Ag==",
"requires": {
"detect-libc": "1.0.3",
"expand-template": "1.1.0",
"github-from-package": "0.0.0",
"minimist": "1.2.0",
"mkdirp": "0.5.1",
"node-abi": "2.1.1",
"node-abi": "2.3.0",
"noop-logger": "0.1.1",
"npmlog": "4.1.2",
"os-homedir": "1.0.2",
"pump": "1.0.2",
"rc": "1.2.2",
"simple-get": "1.4.3",
"pump": "2.0.1",
"rc": "1.2.6",
"simple-get": "2.7.0",
"tar-fs": "1.16.0",
"tunnel-agent": "0.6.0",
"xtend": "4.0.1"
"which-pm-runs": "1.0.0"
}
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"pump": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz",
"integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE=",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
"requires": {
"end-of-stream": "1.4.0",
"end-of-stream": "1.4.1",
"once": "1.4.0"
}
},
"rc": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz",
"integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz",
"integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=",
"requires": {
"deep-extend": "0.4.2",
"ini": "1.3.4",
"ini": "1.3.5",
"minimist": "1.2.0",
"strip-json-comments": "2.0.1"
}
},
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"process-nextick-args": "2.0.0",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"string_decoder": "1.1.1",
"util-deprecate": "1.0.2"
}
},
@@ -274,6 +305,11 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -284,14 +320,19 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"simple-concat": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
},
"simple-get": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-1.4.3.tgz",
"integrity": "sha1-6XVe2kB+ltpAxeUVjJ6jezO+y+s=",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.7.0.tgz",
"integrity": "sha512-RkE9rGPHcxYZ/baYmgJtOSM63vH0Vyq+ma5TijBcLla41SWlh8t6XYIGMR/oeZcmr+/G8k+zrClkkVrtnQ0esg==",
"requires": {
"decompress-response": "3.3.0",
"once": "1.4.0",
"unzip-response": "1.0.2",
"xtend": "4.0.1"
"simple-concat": "1.0.0"
}
},
"string-width": {
@@ -305,9 +346,9 @@
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "5.1.1"
}
@@ -332,18 +373,29 @@
"requires": {
"chownr": "1.0.1",
"mkdirp": "0.5.1",
"pump": "1.0.2",
"tar-stream": "1.5.4"
"pump": "1.0.3",
"tar-stream": "1.5.5"
},
"dependencies": {
"pump": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
"integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
"requires": {
"end-of-stream": "1.4.1",
"once": "1.4.0"
}
}
}
},
"tar-stream": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz",
"integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=",
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz",
"integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==",
"requires": {
"bl": "1.2.1",
"end-of-stream": "1.4.0",
"readable-stream": "2.3.3",
"bl": "1.2.2",
"end-of-stream": "1.4.1",
"readable-stream": "2.3.6",
"xtend": "4.0.1"
}
},
@@ -355,16 +407,16 @@
"safe-buffer": "5.1.1"
}
},
"unzip-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz",
"integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"which-pm-runs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
},
"wide-align": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",

View File

@@ -11,6 +11,7 @@
"@types/node": "8.0.28"
},
"dependencies": {
"lodash-es": "^4.17.10",
"node-hid": "0.5.7",
"uhk-common": "1.0.0"
}

View File

@@ -1,5 +1,6 @@
import { cloneDeep, isEqual } from 'lodash-es';
import { Device, devices, HID } from 'node-hid';
import { LogService } from 'uhk-common';
import { CommandLineArgs, LogService } from 'uhk-common';
import {
ConfigBufferId,
@@ -24,10 +25,12 @@ export class UhkHidDevice {
* Internal variable that represent the USB UHK device
* @private
*/
private _prevDevices = {};
private _device: HID;
private _hasPermission = false;
constructor(private logService: LogService) {
constructor(private logService: LogService,
private options: CommandLineArgs) {
}
/**
@@ -38,6 +41,10 @@ export class UhkHidDevice {
* @returns {boolean}
*/
public hasPermission(): boolean {
if (this.options.spe) {
return false;
}
try {
if (this._hasPermission) {
return true;
@@ -192,7 +199,7 @@ export class UhkHidDevice {
async sendKbootCommandToModule(module: ModuleSlotToI2cAddress, command: KbootCommands, maxTry = 1): Promise<any> {
let transfer;
const moduleName = kbootKommandName(module);
const moduleName = kbootCommandName(module);
this.logService.debug(`[UhkHidDevice] USB[T]: Send KbootCommand ${moduleName} ${KbootCommands[command].toString()}`);
if (command === KbootCommands.idle) {
transfer = new Buffer([UsbCommand.SendKbootCommandToModule, command]);
@@ -228,12 +235,20 @@ export class UhkHidDevice {
private connectToDevice(): HID {
try {
const devs = devices();
if (!isEqual(this._prevDevices, devs)) {
this.logService.debug('[UhkHidDevice] Available devices:', devs);
this._prevDevices = devs;
} else {
this.logService.debug('[UhkHidDevice] Available devices unchanged');
}
const dev = devs.find((x: Device) =>
x.vendorId === Constants.VENDOR_ID &&
x.productId === Constants.PRODUCT_ID &&
((x.usagePage === 128 && x.usage === 129) || x.interface === 0));
// hidapi can not read the interface number on Mac, so check the usage page and usage
((x.usagePage === 128 && x.usage === 129) || // Old firmware
(x.usagePage === (0xFF00 | 0x00) && x.usage === 0x01) || // New firmware
x.interface === 0));
if (!dev) {
this.logService.debug('[UhkHidDevice] UHK Device not found:');
@@ -251,7 +266,7 @@ export class UhkHidDevice {
}
}
function kbootKommandName(module: ModuleSlotToI2cAddress): string {
function kbootCommandName(module: ModuleSlotToI2cAddress): string {
switch (module) {
case ModuleSlotToI2cAddress.leftHalf:
return 'leftHalf';

View File

@@ -105,8 +105,6 @@ export class UhkOperations {
* @returns {Promise<Buffer>}
*/
public async loadConfiguration(configBufferId: ConfigBufferId): Promise<string> {
let response = [];
const configBufferIdToName = ['HardwareConfig', 'StagingUserConfig', 'ValidatedUserConfig'];
const configName = configBufferIdToName[configBufferId];
@@ -140,7 +138,8 @@ export class UhkOperations {
}
}
}
response = convertBufferToIntArray(configBuffer);
const response = convertBufferToIntArray(configBuffer);
return Promise.resolve(JSON.stringify(response));
} catch (error) {
const errMsg = `[DeviceOperation] ${configName} from eeprom error`;
@@ -165,10 +164,10 @@ export class UhkOperations {
return configSize;
}
public async saveUserConfiguration(json: string): Promise<void> {
public async saveUserConfiguration(buffer: Buffer): Promise<void> {
try {
this.logService.debug('[DeviceOperation] USB[T]: Write user configuration to keyboard');
await this.sendUserConfigToKeyboard(json);
await this.sendUserConfigToKeyboard(buffer);
this.logService.debug('[DeviceOperation] USB[T]: Write user configuration to EEPROM');
await this.device.writeConfigToEeprom(ConfigBufferId.validatedUserConfig);
}
@@ -246,12 +245,11 @@ export class UhkOperations {
/**
* IpcMain handler. Send the UserConfiguration to the UHK Device and send a response with the result.
* @param {string} json - UserConfiguration in JSON format
* @param {Buffer} buffer - UserConfiguration buffer
* @returns {Promise<void>}
* @private
*/
private async sendUserConfigToKeyboard(json: string): Promise<void> {
const buffer: Buffer = new Buffer(JSON.parse(json).data);
private async sendUserConfigToKeyboard(buffer: Buffer): Promise<void> {
const fragments = getTransferBuffers(UsbCommand.WriteStagingUserConfig, buffer);
for (const fragment of fragments) {
await this.device.write(fragment);

File diff suppressed because it is too large Load Diff

View File

@@ -10,22 +10,24 @@
"lint": "ng lint",
"e2e": "ng e2e",
"build:renderer": "webpack --config webpack.config.js",
"server:renderer": "webpack --config webpack.config.js --watch"
"server:renderer": "webpack --config webpack.config.js --watch",
"pree2e": "webdriver-manager update --standalone false --gecko false --quiet"
},
"private": true,
"devDependencies": {
"@angular/animations": "4.4.5",
"@angular/cli": "1.4.7",
"@angular/common": "4.4.5",
"@angular/compiler": "4.4.5",
"@angular/compiler-cli": "4.4.5",
"@angular/core": "4.4.5",
"@angular/forms": "4.4.5",
"@angular/http": "4.4.5",
"@angular/language-service": "4.4.5",
"@angular/platform-browser": "4.4.5",
"@angular/platform-browser-dynamic": "4.4.5",
"@angular/router": "4.4.5",
"@angular/animations": "5.2.9",
"@angular/cli": "1.7.4",
"@angular/common": "5.2.9",
"@angular/compiler": "5.2.9",
"@angular/compiler-cli": "5.2.9",
"@angular/core": "5.2.9",
"@angular-devkit/build-optimizer": "0.3.2",
"@angular/forms": "5.2.9",
"@angular/http": "5.2.9",
"@angular/language-service": "5.2.9",
"@angular/platform-browser": "5.2.9",
"@angular/platform-browser-dynamic": "5.2.9",
"@angular/router": "5.2.9",
"@ngrx/effects": "4.0.5",
"@ngrx/router-store": "4.0.4",
"@ngrx/store": "4.0.3",
@@ -38,64 +40,44 @@
"@types/jasminewd2": "2.0.2",
"@types/jquery": "3.2.9",
"@types/lodash-es": "4.17.0",
"@types/node-hid": "0.5.2",
"@types/usb": "1.1.3",
"angular-confirmation-popover": "3.2.0",
"angular-notifier": "2.0.0",
"autoprefixer": "6.5.3",
"autoprefixer": "^7.2.3",
"bootstrap": "3.3.7",
"buffer": "5.0.6",
"circular-dependency-plugin": "3.0.0",
"circular-dependency-plugin": "^4.2.1",
"codelyzer": "3.0.1",
"copy-webpack-plugin": "4.0.1",
"css-loader": "0.28.1",
"cssnano": "3.10.0",
"dragula": "3.7.2",
"exports-loader": "0.6.3",
"file-loader": "0.10.0",
"file-saver": "1.3.3",
"font-awesome": "4.7.0",
"html-webpack-plugin": "2.29.0",
"istanbul-instrumenter-loader": "2.0.0",
"html-webpack-plugin": "^2.29.0",
"jasmine-core": "2.6.2",
"jasmine-spec-reporter": "4.1.0",
"jquery": "3.2.1",
"jsonfile": "3.0.1",
"karma": "1.7.0",
"karma-chrome-launcher": "2.1.1",
"karma-cli": "1.0.1",
"karma-coverage-istanbul-reporter": "1.2.1",
"karma-jasmine": "1.1.0",
"karma-jasmine-html-reporter": "0.2.2",
"less-loader": "4.0.5",
"lodash-es": "4.17.4",
"ng2-dragula": "1.5.0",
"ng2-nouislider": "^1.7.6",
"ng2-nouislider": "^1.7.7",
"ng2-select2": "1.0.0-beta.10",
"ngx-clipboard": "10.0.0",
"ngrx-store-freeze": "0.1.9",
"node-hid": "0.5.4",
"nouislider": "^10.1.0",
"postcss-loader": "1.3.3",
"postcss-url": "5.1.2",
"nouislider": "^11.1.0",
"postcss-url": "^7.1.2",
"protractor": "5.1.2",
"raw-loader": "0.5.1",
"reselect": "3.0.1",
"sass-loader": "6.0.3",
"script-loader": "0.7.0",
"rxjs": "5.5.8",
"select2": "4.0.3",
"source-map-loader": "0.2.0",
"style-loader": "0.13.1",
"stylus-loader": "3.0.1",
"sudo-prompt": "7.1.1",
"ts-loader": "2.3.1",
"ts-node": "3.0.4",
"typescript": "2.6.2",
"uhk-common": "1.0.0",
"url-loader": "0.5.7",
"webpack": "3.4.1",
"webpack-dev-server": "2.5.1",
"webpack-svgstore-plugin": "4.0.1",
"xml-loader": "1.2.1",
"zone.js": "0.8.14"
"zone.js": "0.8.26",
"@angular-devkit/core": "0.3.2",
"@ngtools/webpack": "1.10.2"
},
"dependencies": {
"classlist.js": "1.1.20150312",

View File

@@ -1,4 +1,4 @@
import { Component, HostListener, ViewEncapsulation } from '@angular/core';
import { Component, ViewEncapsulation } from '@angular/core';
import { animate, style, transition, trigger } from '@angular/animations';
import { Observable } from 'rxjs/Observable';
import { Action, Store } from '@ngrx/store';
@@ -14,7 +14,6 @@ import {
saveToKeyboardState
} from './store';
import { ProgressButtonState } from './store/reducers/progress-button-state';
import { SaveUserConfigInBinaryFileAction, SaveUserConfigInJsonFileAction } from './store/actions/user-config';
@Component({
selector: 'main-app',

View File

@@ -4,6 +4,7 @@ import { DeviceConfigurationComponent } from './configuration/device-configurati
import { DeviceFirmwareComponent } from './firmware/device-firmware.component';
import { MouseSpeedComponent } from './mouse-speed/mouse-speed.component';
import { LEDBrightnessComponent } from './led-brightness/led-brightness.component';
import { RestoreConfigurationComponent } from './restore-configuration/restore-configuration.component';
export const deviceRoutes: Routes = [
{
@@ -29,6 +30,10 @@ export const deviceRoutes: Routes = [
{
path: 'firmware',
component: DeviceFirmwareComponent
},
{
path: 'restore-user-configuration',
component: RestoreConfigurationComponent
}
]
}

View File

@@ -2,4 +2,5 @@ export * from './configuration/device-configuration.component';
export * from './firmware/device-firmware.component';
export * from './mouse-speed/mouse-speed.component';
export * from './led-brightness/led-brightness.component';
export * from './restore-configuration/restore-configuration.component';
export * from './device.routes';

View File

@@ -0,0 +1,19 @@
<h1>
<i class="fa fa-exclamation-circle"></i>
<span>Fix configuration</span>
</h1>
<p>
Your on-board device configuration is invalid.
</p>
<button class="btn btn-primary"
*ngIf="state.hasBackupUserConfiguration"
[disabled]="state.restoringUserConfiguration"
(click)="restoreUserConfiguration()"> Restore the last valid device configuration
</button>
<button class="btn btn-danger"
*ngIf="!state.hasBackupUserConfiguration"
[disabled]="state.restoringUserConfiguration"
(click)="resetUserConfiguration()">Reset device configuration
</button>

View File

@@ -0,0 +1,10 @@
:host {
overflow-y: auto;
display: block;
height: 100%;
width: 100%;
p {
margin: 1.5rem 0;
}
}

View File

@@ -0,0 +1,48 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs/Subscription';
import { AppState, getBackupUserConfigurationState } from '../../../store';
import { ResetUserConfigurationAction, RestoreUserConfigurationFromBackupAction } from '../../../store/actions/device';
import { RestoreConfigurationState } from '../../../models/restore-configuration-state';
@Component({
selector: 'restore-configuration',
templateUrl: './restore-configuration.component.html',
styleUrls: ['./restore-configuration.component.scss'],
host: {
'class': 'container-fluid'
}
})
export class RestoreConfigurationComponent implements OnInit, OnDestroy {
state: RestoreConfigurationState;
private stateSubscription: Subscription;
constructor(private store: Store<AppState>,
private cdRef: ChangeDetectorRef) {
}
ngOnDestroy(): void {
if (this.stateSubscription) {
this.stateSubscription.unsubscribe();
}
}
ngOnInit(): void {
this.stateSubscription = this.store
.select(getBackupUserConfigurationState)
.subscribe(data => {
this.state = data;
this.cdRef.markForCheck();
});
}
resetUserConfiguration() {
this.store.dispatch(new ResetUserConfigurationAction());
}
restoreUserConfiguration(): void {
this.store.dispatch(new RestoreUserConfigurationFromBackupAction());
}
}

View File

@@ -67,7 +67,7 @@ export class MacroKeyTabComponent extends MacroBaseComponent implements OnInit {
}
getKeyMacroAction(): KeyMacroAction {
const keyMacroAction = Object.assign(new KeyMacroAction(), this.keypressTab.toKeyAction());
const keyMacroAction = new KeyMacroAction(this.keypressTab.toKeyAction() as any);
keyMacroAction.action = this.getActionType(this.activeTab);
return keyMacroAction;
}

View File

@@ -1,5 +1,6 @@
<div class="row list-container">
<div class="col-xs-10 col-xs-offset-1 list-group">
<p><i>Please note that macro playback is not implemented yet. You can create macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p>
<div class="macro-actions-container" [dragula]="'macroActions'" [dragulaModel]="macro.macroActions">
<macro-item *ngFor="let macroAction of macro.macroActions; let macroActionIndex = index"
[macroAction]="macroAction"

View File

@@ -79,8 +79,8 @@ export class PopoverComponent implements OnChanges {
@Input() defaultKeyAction: KeyAction;
@Input() currentKeymap: Keymap;
@Input() currentLayer: number;
@Input() keyPosition: ClientRect;
@Input() wrapPosition: ClientRect;
@Input() keyPosition: any;
@Input() wrapPosition: any;
@Input() visible: boolean;
@Output() cancel = new EventEmitter<any>();

View File

@@ -9,7 +9,7 @@
></select2>
<icon name="question-circle"
data-toggle="tooltip"
title="Looking for a non-US character? Just pick the character of the desired key according to the US layout."
title="Looking for a non-US character? Just pick the character of the desired key according to the US layout. For example, on US keyboards next to Tab there is the Q key, but it's й on Russian keyboards, so in this case choose Q instead of й in Agent."
data-placement="bottom"></icon>
<capture-keystroke-button (capture)="onKeysCapture($event)" tabindex="0"></capture-keystroke-button>
</div>

View File

@@ -115,7 +115,7 @@ export class KeypressTabComponent extends Tab implements OnChanges {
const scTypePair = this.toScancodeTypePair(this.selectedScancodeOption);
keystrokeAction.scancode = scTypePair[0];
if (scTypePair[1] === 'media') {
keystrokeAction.type = KeystrokeType.shortMedia;
keystrokeAction.type = keystrokeAction.scancode > 255 ? KeystrokeType.longMedia : KeystrokeType.shortMedia;
} else {
keystrokeAction.type = KeystrokeType[scTypePair[1]];
}

View File

@@ -11,7 +11,7 @@
</option>
</select>
<span [ngSwitch]="toggle">
<ng-template [ngSwitchCase]="true">layer by pressing this key.</ng-template>
<ng-template [ngSwitchCase]="true">layer by tapping this key.</ng-template>
<ng-template ngSwitchDefault>layer by holding this key.</ng-template>
</span>
</ng-template>

View File

@@ -2,7 +2,7 @@
<span> No macros are available to choose from. Create a macro first! </span>
</ng-template>
<ng-template [ngIf]="macroOptions.length > 0">
<p><i>Please note that macro playback is not implemented yet. You can bind macros, but they don't have any effect.</i></p>
<p><i>Please note that macro playback is not implemented yet. You can bind macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p>
<div class="macro-selector">
<b> Play macro: </b>
<select2 [data]="macroOptions" [value]="macroOptions[selectedMacroIndex].id" (valueChanged)="onChange($event)" [width]="'100%'"></select2>

View File

@@ -1,4 +1,39 @@
<span class="privilege-checker-wrapper">
<uhk-message header="Cannot talk to your UHK" subtitle="Your UHK has been detected, but its permissions are not set up yet, so Agent can't talk to it."></uhk-message>
<button class="btn btn-default btn-lg btn-primary" (click)="setUpPermissions()"> Set up permissions </button>
</span>
<div class="privilege-checker-wrapper">
<uhk-message header="Cannot talk to your UHK"
subtitle="Your UHK has been detected, but its permissions are not set up yet, so Agent can't talk to it."></uhk-message>
<button class="btn btn-default btn-lg btn-primary"
(click)="setUpPermissions()"> Set up permissions
</button>
<div class="mt-10">
<a class="link-inline"
*ngIf="state.showWhatWillThisDo"
(click)="whatWillThisDo()">What will this do?
</a>
<div>
<p class="privilege-error"
#privilegeError
*ngIf="state.permissionSetupFailed">
Agent wasn't able to set up permissions via PolicyKit. This is most likely because the
<code>polkit</code> package is not installed on your system.
</p>
<div *ngIf="state.showWhatWillThisDoContent">
Agent uses the following script to set up permissions. You can run it manually as root, then
<a class="link-inline"
(click)="retry()">retry</a>.
<div class="copy-container">
<span class="fa fa-2x fa-copy"
ngxClipboard
[cbContent]="command"
title="Copy to clipboard"
data-toggle="tooltip"
data-placement="top"></span>
<pre><code>{{ command }}</code></pre>
</div>
</div>
</div>
</div>
</div>

View File

@@ -9,3 +9,19 @@
uhk-message {
max-width: 50%;
}
.privilege-error {
animation: error-fade-in 2s;
}
@keyframes error-fade-in {
0% {
color: white;
background-color: red;
}
100% {
color: inherit;
background-color: inherit;
}
}

View File

@@ -1,26 +1,61 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/ignoreElements';
import 'rxjs/add/operator/takeWhile';
import { Subscription } from 'rxjs/Subscription';
import { AppState } from '../../store/index';
import { AppState, getPrivilegePageState } from '../../store';
import { SetPrivilegeOnLinuxAction } from '../../store/actions/device';
import { LoadAppStartInfoAction, PrivilegeWhatWillThisDoAction } from '../../store/actions/app';
import { PrivilagePageSate } from '../../models/privilage-page-sate';
@Component({
selector: 'privilege-checker',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './privilege-checker.component.html',
styleUrls: ['./privilege-checker.component.scss']
})
export class PrivilegeCheckerComponent {
constructor(protected store: Store<AppState>) {
export class PrivilegeCheckerComponent implements OnInit, OnDestroy {
state: PrivilagePageSate;
command = `cat <<EOF >/etc/udev/rules.d/50-uhk60.rules
# Ultimate Hacking Keyboard rules
# These are the udev rules for accessing the USB interfaces of the UHK as non-root users.
# Copy this file to /etc/udev/rules.d and physically reconnect the UHK afterwards.
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE:="0666"
EOF
udevadm trigger
udevadm settle`;
private stateSubscription: Subscription;
constructor(private store: Store<AppState>,
private cdRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.stateSubscription = this.store.select(getPrivilegePageState)
.subscribe(state => {
this.state = state;
this.cdRef.markForCheck();
});
}
ngOnDestroy(): void {
if (this.stateSubscription) {
this.stateSubscription.unsubscribe();
}
}
setUpPermissions(): void {
this.store.dispatch(new SetPrivilegeOnLinuxAction());
}
whatWillThisDo(): void {
this.store.dispatch(new PrivilegeWhatWillThisDoAction());
}
retry(): void {
this.store.dispatch(new LoadAppStartInfoAction());
}
}

View File

@@ -5,6 +5,7 @@
<input #deviceName cancelable
class="pane-title__name"
type="text"
[readonly]="state.restoreUserConfiguration"
(change)="editDeviceName($event.target.value)"
(keyup.enter)="deviceName.blur()"
(keyup)="calculateHeaderTextWidth($event.target.value)">
@@ -17,33 +18,43 @@
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'configuration')"></i>
</div>
<ul [@toggler]="animation['configuration']">
<li class="sidebar__level-2--item">
<li class="sidebar__level-2--item"
*ngIf="!state.restoreUserConfiguration">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/device/mouse-speed']"
[class.disabled]="updatingFirmware$ | async">Mouse speed</a>
[class.disabled]="state.updatingFirmware">Mouse speed</a>
</div>
</li>
<li class="sidebar__level-2--item">
<li class="sidebar__level-2--item"
*ngIf="!state.restoreUserConfiguration">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/device/led-brightness']"
[class.disabled]="updatingFirmware$ | async">LED brightness</a>
[class.disabled]="state.updatingFirmware">LED brightness</a>
</div>
</li>
<li class="sidebar__level-2--item">
<li class="sidebar__level-2--item"
*ngIf="!state.restoreUserConfiguration">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/device/configuration']"
[class.disabled]="updatingFirmware$ | async">Configuration</a>
[class.disabled]="state.updatingFirmware">Configuration</a>
</div>
</li>
<li class="sidebar__level-2--item"
*ngIf="state.restoreUserConfiguration">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/device/restore-user-configuration']">Fix configuration</a>
</div>
</li>
<li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/device/firmware']"
[class.disabled]="updatingFirmware$ | async">Firmware</a>
[class.disabled]="state.updatingFirmware">Firmware</a>
</div>
</li>
</ul>
</li>
<li class="sidebar__level-1--item">
<li class="sidebar__level-1--item"
*ngIf="!state.restoreUserConfiguration">
<div class="sidebar__level-1">
<i class="fa fa-keyboard-o"></i> Keymaps
<!--a [routerLink]="['/keymap/add']"
@@ -55,10 +66,10 @@
(click)="toggleHide($event, 'keymap')"></i>
</div>
<ul [@toggler]="animation['keymap']">
<li *ngFor="let keymap of keymaps$ | async" class="sidebar__level-2--item">
<li *ngFor="let keymap of state.keymaps" class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/keymap', keymap.abbreviation]"
[class.disabled]="updatingFirmware$ | async">{{keymap.name}}</a>
[class.disabled]="state.updatingFirmware">{{keymap.name}}</a>
<i *ngIf="keymap.isDefault" class="fa fa-star sidebar__fav"
title="This is the default keymap which gets activated when powering the keyboard."
data-toggle="tooltip" data-placement="bottom"></i>
@@ -66,26 +77,27 @@
</li>
</ul>
</li>
<li class="sidebar__level-1--item">
<li class="sidebar__level-1--item"
*ngIf="!state.restoreUserConfiguration">
<div class="sidebar__level-1">
<i class="fa fa-play"></i> Macros
<a (click)="addMacro()"
class="btn btn-default pull-right btn-sm"
[class.disabled]="updatingFirmware$ | async">
[class.disabled]="state.updatingFirmware">
<i class="fa fa-plus"></i>
</a>
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'macro')"></i>
</div>
<ul [@toggler]="animation['macro']">
<li *ngFor="let macro of macros$ | async" class="sidebar__level-2--item">
<li *ngFor="let macro of state.macros" class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/macro', macro.id]"
[class.disabled]="updatingFirmware$ | async">{{macro.name}}</a>
[class.disabled]="state.updatingFirmware">{{macro.name}}</a>
</div>
</li>
</ul>
</li>
<li class="sidebar__level-1--item" *ngIf="showAddonMenu$ | async">
<li class="sidebar__level-1--item" *ngIf="state.showAddonMenu">
<div class="sidebar__level-1">
<i class="fa fa-puzzle-piece"></i> Add-on modules
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'addon')"></i>
@@ -94,25 +106,25 @@
<li class="sidebar__level-2--item" data-name="Key cluster" data-abbrev="">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/add-on', 'Key cluster']"
[class.disabled]="updatingFirmware$ | async">Key cluster</a>
[class.disabled]="state.updatingFirmware">Key cluster</a>
</div>
</li>
<li class="sidebar__level-2--item" data-name="Trackball" data-abbrev="">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/add-on', 'Trackball']"
[class.disabled]="updatingFirmware$ | async">Trackball</a>
[class.disabled]="state.updatingFirmware">Trackball</a>
</div>
</li>
<li class="sidebar__level-2--item" data-name="Toucpad" data-abbrev="">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/add-on', 'Touchpad']"
[class.disabled]="updatingFirmware$ | async">Touchpad</a>
[class.disabled]="state.updatingFirmware">Touchpad</a>
</div>
</li>
<li class="sidebar__level-2--item" data-name="Trackpoint" data-abbrev="">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/add-on', 'Trackpoint']"
[class.disabled]="updatingFirmware$ | async">Trackpoint</a>
[class.disabled]="state.updatingFirmware">Trackpoint</a>
</div>
</li>
</ul>
@@ -129,13 +141,13 @@
<li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/settings']"
[class.disabled]="updatingFirmware$ | async">Settings</a>
[class.disabled]="state.updatingFirmware">Settings</a>
</div>
</li>
<li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/about']"
[class.disabled]="updatingFirmware$ | async">About</a>
[class.disabled]="state.updatingFirmware">About</a>
</div>
</li>
</ul>

View File

@@ -1,20 +1,27 @@
import { AfterContentInit, Component, ElementRef, OnDestroy, Renderer2, ViewChild } from '@angular/core';
import {
AfterContentInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
OnDestroy, OnInit,
Renderer2,
ViewChild
} from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { Keymap, Macro } from 'uhk-common';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/let';
import { AppState, getDeviceName, runningInElectron, showAddonMenu, updatingFirmware } from '../../store';
import { AppState, getSideMenuPageState } from '../../store';
import { MacroActions } from '../../store/actions';
import { getKeymaps, getMacros } from '../../store/reducers/user-configuration';
import * as util from '../../util';
import { RenameUserConfigurationAction } from '../../store/actions/user-config';
import { SideMenuPageState } from '../../models/side-menu-page-state';
@Component({
animations: [
@@ -30,24 +37,19 @@ import { RenameUserConfigurationAction } from '../../store/actions/user-config';
],
selector: 'side-menu',
templateUrl: './side-menu.component.html',
styleUrls: ['./side-menu.component.scss']
styleUrls: ['./side-menu.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SideMenuComponent implements AfterContentInit, OnDestroy {
showAddonMenu$: Observable<boolean>;
runInElectron$: Observable<boolean>;
updatingFirmware$: Observable<boolean>;
deviceName$: Observable<string>;
deviceNameSubscription: Subscription;
keymaps$: Observable<Keymap[]>;
macros$: Observable<Macro[]>;
export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy {
state: SideMenuPageState;
animation: { [key: string]: 'active' | 'inactive' };
deviceNameValue: string;
updatingFirmware = false;
updatingFirmwareSubscription: Subscription;
@ViewChild('deviceName') deviceName: ElementRef;
constructor(private store: Store<AppState>, private renderer: Renderer2) {
private stateSubscription: Subscription;
constructor(private store: Store<AppState>,
private renderer: Renderer2,
private cdRef: ChangeDetectorRef) {
this.animation = {
device: 'active',
configuration: 'active',
@@ -55,20 +57,13 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
macro: 'active',
addon: 'active'
};
}
this.keymaps$ = store.let(getKeymaps());
this.macros$ = store.let(getMacros());
this.showAddonMenu$ = this.store.select(showAddonMenu);
this.runInElectron$ = this.store.select(runningInElectron);
this.deviceName$ = store.select(getDeviceName);
this.deviceNameSubscription = this.deviceName$.subscribe(name => {
this.deviceNameValue = name;
ngOnInit(): void {
this.stateSubscription = this.store.select(getSideMenuPageState).subscribe(data => {
this.state = data;
this.setDeviceName();
});
this.updatingFirmware$ = store.select(updatingFirmware);
this.updatingFirmwareSubscription = this.updatingFirmware$.subscribe(updating => {
this.updatingFirmware = updating;
this.cdRef.markForCheck();
});
}
@@ -77,12 +72,13 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
}
ngOnDestroy(): void {
this.deviceNameSubscription.unsubscribe();
this.updatingFirmwareSubscription.unsubscribe();
if (this.stateSubscription) {
this.stateSubscription.unsubscribe();
}
}
toggleHide(event: Event, type: string) {
if (this.updatingFirmware) {
if (this.state.updatingFirmware) {
return;
}
@@ -110,7 +106,7 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
}
editDeviceName(name: string): void {
if (!util.isValidName(name) || name.trim() === this.deviceNameValue) {
if (!util.isValidName(name) || name.trim() === this.state.deviceName) {
this.setDeviceName();
return;
}
@@ -126,7 +122,7 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
private setDeviceName(): void {
if (this.deviceName) {
this.renderer.setProperty(this.deviceName.nativeElement, 'value', this.deviceNameValue);
this.renderer.setProperty(this.deviceName.nativeElement, 'value', this.state.deviceName);
this.calculateHeaderTextWidth(this.deviceName.nativeElement.value);
}
}

View File

@@ -1,6 +1,6 @@
import { AfterViewInit, Component, EventEmitter, forwardRef, Input, Output, OnDestroy, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { NouisliderComponent } from 'ng2-nouislider/src/nouislider';
import { NouisliderComponent } from 'ng2-nouislider';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import 'rxjs/add/operator/debounceTime';

View File

@@ -0,0 +1,5 @@
export interface PrivilagePageSate {
showWhatWillThisDo: boolean;
showWhatWillThisDoContent: boolean;
permissionSetupFailed: boolean;
}

View File

@@ -0,0 +1,4 @@
export interface RestoreConfigurationState {
restoringUserConfiguration: boolean;
hasBackupUserConfiguration: boolean;
}

View File

@@ -0,0 +1,11 @@
import { Keymap, Macro } from 'uhk-common';
export interface SideMenuPageState {
showAddonMenu: boolean;
runInElectron: boolean;
updatingFirmware: boolean;
deviceName: string;
keymaps: Keymap[];
macros: Macro[];
restoreUserConfiguration: boolean;
}

View File

@@ -1,7 +1,7 @@
import { Injectable, NgZone } from '@angular/core';
import { Action, Store } from '@ngrx/store';
import { DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
import { DeviceConnectionState, IpcEvents, IpcResponse, LogService, SaveUserConfigurationData } from 'uhk-common';
import { AppState } from '../store';
import { IpcCommonRenderer } from './ipc-common-renderer';
import {
@@ -26,8 +26,8 @@ export class DeviceRendererService {
this.ipcRenderer.send(IpcEvents.device.setPrivilegeOnLinux);
}
saveUserConfiguration(buffer: Buffer): void {
this.ipcRenderer.send(IpcEvents.device.saveUserConfiguration, JSON.stringify(buffer));
saveUserConfiguration(data: SaveUserConfigurationData): void {
this.ipcRenderer.send(IpcEvents.device.saveUserConfiguration, JSON.stringify(data));
}
loadConfigurationFromKeyboard(): void {

View File

@@ -7,11 +7,11 @@
"iconsAndLayerTextsBrightness": 255,
"alphanumericSegmentsBrightness": 255,
"keyBacklightBrightness": 255,
"mouseMoveInitialSpeed": 5,
"mouseMoveAcceleration": 35,
"mouseMoveDeceleratedSpeed": 10,
"mouseMoveBaseSpeed": 40,
"mouseMoveAcceleratedSpeed": 80,
"mouseMoveInitialSpeed": 4,
"mouseMoveAcceleration": 68,
"mouseMoveDeceleratedSpeed": 8,
"mouseMoveBaseSpeed": 32,
"mouseMoveAcceleratedSpeed": 64,
"mouseScrollInitialSpeed": 20,
"mouseScrollAcceleration": 20,
"mouseScrollDeceleratedSpeed": 20,

View File

@@ -8,6 +8,7 @@ import { ConfirmationPopoverModule } from 'angular-confirmation-popover';
import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { Select2Module } from 'ng2-select2/ng2-select2';
import { NouisliderModule } from 'ng2-nouislider';
import { ClipboardModule } from 'ngx-clipboard';
import { AddOnComponent } from './components/add-on';
import { KeyboardSliderComponent } from './components/keyboard/slider';
@@ -15,7 +16,8 @@ import {
DeviceConfigurationComponent,
DeviceFirmwareComponent,
MouseSpeedComponent,
LEDBrightnessComponent
LEDBrightnessComponent,
RestoreConfigurationComponent
} from './components/device';
import { KeymapAddComponent, KeymapEditComponent, KeymapHeaderComponent } from './components/keymap';
import { LayersComponent } from './components/layers';
@@ -173,7 +175,8 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
XtermComponent,
SliderWrapperComponent,
EditableTextComponent,
Autofocus
Autofocus,
RestoreConfigurationComponent
],
imports: [
CommonModule,
@@ -186,7 +189,8 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
NotifierModule.withConfig(angularNotifierConfig),
ConfirmationPopoverModule.forRoot({
confirmButtonType: 'danger' // set defaults here
})
}),
ClipboardModule
],
providers: [
SvgModuleProviderService,

View File

@@ -17,7 +17,10 @@ export const ActionTypes = {
DISMISS_UNDO_NOTIFICATION: type(PREFIX + 'dismiss notification action'),
LOAD_HARDWARE_CONFIGURATION_SUCCESS: type(PREFIX + 'load hardware configuration success'),
ELECTRON_MAIN_LOG_RECEIVED: type(PREFIX + 'Electron main log received'),
OPEN_URL_IN_NEW_WINDOW: type(PREFIX + 'Open URL in new Window')
OPEN_URL_IN_NEW_WINDOW: type(PREFIX + 'Open URL in new Window'),
PRIVILEGE_WHAT_WILL_THIS_DO: type(PREFIX + 'What will this do clicked'),
SETUP_PERMISSION_ERROR: type(PREFIX + 'Setup permission error'),
LOAD_APP_START_INFO: type(PREFIX + 'Load app start info')
};
export class AppBootsrappedAction implements Action {
@@ -31,25 +34,29 @@ export class AppStartedAction implements Action {
export class ShowNotificationAction implements Action {
type = ActionTypes.APP_SHOW_NOTIFICATION;
constructor(public payload: Notification) { }
constructor(public payload: Notification) {
}
}
export class ApplyCommandLineArgsAction implements Action {
type = ActionTypes.APPLY_COMMAND_LINE_ARGS;
constructor(public payload: CommandLineArgs) { }
constructor(public payload: CommandLineArgs) {
}
}
export class ProcessAppStartInfoAction implements Action {
type = ActionTypes.APP_PROCESS_START_INFO;
constructor(public payload: AppStartInfo) { }
constructor(public payload: AppStartInfo) {
}
}
export class UndoLastAction implements Action {
type = ActionTypes.UNDO_LAST;
constructor(public payload: any) {}
constructor(public payload: any) {
}
}
export class UndoLastSuccessAction implements Action {
@@ -63,19 +70,37 @@ export class DismissUndoNotificationAction implements Action {
export class LoadHardwareConfigurationSuccessAction implements Action {
type = ActionTypes.LOAD_HARDWARE_CONFIGURATION_SUCCESS;
constructor(public payload: HardwareConfiguration) {}
constructor(public payload: HardwareConfiguration) {
}
}
export class ElectronMainLogReceivedAction implements Action {
type = ActionTypes.ELECTRON_MAIN_LOG_RECEIVED;
constructor(public payload: ElectronLogEntry) {}
constructor(public payload: ElectronLogEntry) {
}
}
export class OpenUrlInNewWindowAction implements Action {
type = ActionTypes.OPEN_URL_IN_NEW_WINDOW;
constructor(public payload: string) {}
constructor(public payload: string) {
}
}
export class PrivilegeWhatWillThisDoAction implements Action {
type = ActionTypes.PRIVILEGE_WHAT_WILL_THIS_DO;
}
export class SetupPermissionErrorAction implements Action {
type = ActionTypes.SETUP_PERMISSION_ERROR;
constructor(public payload: string) {
}
}
export class LoadAppStartInfoAction implements Action {
type = ActionTypes.LOAD_APP_START_INFO;
}
export type Actions
@@ -90,4 +115,7 @@ export type Actions
| LoadHardwareConfigurationSuccessAction
| ElectronMainLogReceivedAction
| OpenUrlInNewWindowAction
| PrivilegeWhatWillThisDoAction
| SetupPermissionErrorAction
| LoadAppStartInfoAction
;

View File

@@ -1,6 +1,5 @@
import { Action } from '@ngrx/store';
import { DeviceConnectionState, IpcResponse, type } from 'uhk-common';
import { HardwareModules } from '../../../../../uhk-common/src/models';
import { DeviceConnectionState, HardwareModules, IpcResponse, type } from 'uhk-common';
const PREFIX = '[device] ';
@@ -24,7 +23,10 @@ export const ActionTypes = {
UPDATE_FIRMWARE_SUCCESS: type(PREFIX + 'update firmware success'),
UPDATE_FIRMWARE_FAILED: type(PREFIX + 'update firmware failed'),
UPDATE_FIRMWARE_OK_BUTTON: type(PREFIX + 'update firmware ok button click'),
MODULES_INFO_LOADED: type(PREFIX + 'module info loaded')
MODULES_INFO_LOADED: type(PREFIX + 'module info loaded'),
HAS_BACKUP_USER_CONFIGURATION: type(PREFIX + 'Store backup user configuration'),
RESTORE_CONFIGURATION_FROM_BACKUP: type(PREFIX + 'Restore configuration from backup'),
RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS: type(PREFIX + 'Restore configuration from backup success')
};
export class SetPrivilegeOnLinuxAction implements Action {
@@ -123,6 +125,21 @@ export class HardwareModulesLoadedAction implements Action {
}
}
export class RestoreUserConfigurationFromBackupAction implements Action {
type = ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP;
}
export class HasBackupUserConfigurationAction implements Action {
type = ActionTypes.HAS_BACKUP_USER_CONFIGURATION;
constructor(public payload: boolean) {
}
}
export class RestoreUserConfigurationFromBackupSuccessAction implements Action {
type = ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS;
}
export type Actions
= SetPrivilegeOnLinuxAction
| SetPrivilegeOnLinuxReplyAction
@@ -142,4 +159,7 @@ export type Actions
| UpdateFirmwareFailedAction
| UpdateFirmwareOkButtonAction
| HardwareModulesLoadedAction
| RestoreUserConfigurationFromBackupAction
| HasBackupUserConfigurationAction
| RestoreUserConfigurationFromBackupSuccessAction
;

View File

@@ -40,6 +40,13 @@ export class ApplicationEffects {
this.logService.info('Renderer appStart effect end');
});
@Effect({dispatch: false})
appStartInfo$: Observable<Action> = this.actions$
.ofType(ActionTypes.LOAD_APP_START_INFO)
.do(() => {
this.appRendererService.getAppStartInfo();
});
@Effect({dispatch: false})
showNotification$: Observable<Action> = this.actions$
.ofType<ShowNotificationAction>(ActionTypes.APP_SHOW_NOTIFICATION)

View File

@@ -13,11 +13,19 @@ import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/switchMap';
import { DeviceConnectionState, IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common';
import {
DeviceConnectionState,
HardwareConfiguration,
IpcResponse,
NotificationType,
UserConfiguration
} from 'uhk-common';
import {
ActionTypes,
ConnectionStateChangedAction,
HideSaveToKeyboardButton,
ResetUserConfigurationAction,
RestoreUserConfigurationFromBackupSuccessAction,
SaveConfigurationAction,
SaveConfigurationReplyAction,
SaveToKeyboardSuccessAction,
@@ -31,7 +39,7 @@ import {
UpdateFirmwareWithAction
} from '../actions/device';
import { DeviceRendererService } from '../../services/device-renderer.service';
import { ShowNotificationAction } from '../actions/app';
import { SetupPermissionErrorAction, ShowNotificationAction } from '../actions/app';
import { AppState } from '../index';
import {
ActionTypes as UserConfigActions,
@@ -78,30 +86,23 @@ export class DeviceEffects {
setPrivilegeOnLinuxReply$: Observable<Action> = this.actions$
.ofType<SetPrivilegeOnLinuxReplyAction>(ActionTypes.SET_PRIVILEGE_ON_LINUX_REPLY)
.map(action => action.payload)
.mergeMap((response: any) => {
.map((response: any): any => {
if (response.success) {
return [
new ConnectionStateChangedAction({
return new ConnectionStateChangedAction({
connected: true,
hasPermission: true
})
];
});
}
return [
<any>new ShowNotificationAction({
type: NotificationType.Error,
message: response.error.message || response.error
})
];
return new SetupPermissionErrorAction(response.error);
});
@Effect({dispatch: false})
saveConfiguration$: Observable<Action> = this.actions$
.ofType(ActionTypes.SAVE_CONFIGURATION)
.withLatestFrom(this.store)
.map(([action, state]) => state.userConfiguration)
.do((userConfiguration: UserConfiguration) => {
setTimeout(() => this.sendUserConfigToKeyboard(userConfiguration), 100);
.do(([action, state]) => {
setTimeout(() => this.sendUserConfigToKeyboard(state.userConfiguration, state.app.hardwareConfig), 100);
})
.switchMap(() => Observable.empty());
@@ -128,8 +129,18 @@ export class DeviceEffects {
@Effect()
autoHideSaveToKeyboardButton$: Observable<Action> = this.actions$
.ofType(ActionTypes.SAVE_TO_KEYBOARD_SUCCESS)
.switchMap(() => Observable.timer(1000)
.switchMap(() => Observable.of(new HideSaveToKeyboardButton()))
.withLatestFrom(this.store)
.switchMap(([action, state]) => Observable.timer(1000)
.mergeMap(() => {
const actions = [new HideSaveToKeyboardButton()];
if (state.device.hasBackupUserConfiguration) {
actions.push(new RestoreUserConfigurationFromBackupSuccessAction());
this.router.navigate(['/']);
}
return actions;
})
);
@Effect()
@@ -199,6 +210,10 @@ export class DeviceEffects {
.ofType<UpdateFirmwareOkButtonAction>(ActionTypes.UPDATE_FIRMWARE_OK_BUTTON)
.do(() => this.deviceRendererService.startConnectionPoller());
@Effect() restoreUserConfiguration$ = this.actions$
.ofType<ResetUserConfigurationAction>(ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP)
.map(() => new SaveConfigurationAction());
constructor(private actions$: Actions,
private router: Router,
private deviceRendererService: DeviceRendererService,
@@ -207,9 +222,10 @@ export class DeviceEffects {
private defaultUserConfigurationService: DefaultUserConfigurationService) {
}
private sendUserConfigToKeyboard(userConfiguration: UserConfiguration): void {
const uhkBuffer = new UhkBuffer();
userConfiguration.toBinary(uhkBuffer);
this.deviceRendererService.saveUserConfiguration(uhkBuffer.getBufferContent());
private sendUserConfigToKeyboard(userConfiguration: UserConfiguration, hardwareConfig: HardwareConfiguration): void {
this.deviceRendererService.saveUserConfiguration({
uniqueId: hardwareConfig && hardwareConfig.uniqueId,
configuration: userConfiguration.toJsonObject()
});
}
}

View File

@@ -15,8 +15,9 @@ import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import {
getHardwareConfigFromDeviceResponse,
getUserConfigFromDeviceResponse,
ConfigurationReply,
HardwareConfiguration,
LogService,
NotificationType,
UhkBuffer,
@@ -43,7 +44,11 @@ import {
ShowNotificationAction,
UndoLastAction
} from '../actions/app';
import { HardwareModulesLoadedAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
import {
HardwareModulesLoadedAction,
ShowSaveToKeyboardButtonAction,
HasBackupUserConfigurationAction
} from '../actions/device';
import { DeviceRendererService } from '../../services/device-renderer.service';
import { UndoUserConfigData } from '../../models/undo-user-config-data';
import { UploadFileData } from '../../models/upload-file-data';
@@ -51,29 +56,6 @@ import { UploadFileData } from '../../models/upload-file-data';
@Injectable()
export class UserConfigEffects {
private static getUserConfigFromDeviceResponse(json: string): UserConfiguration {
const data = JSON.parse(json);
const userConfig = new UserConfiguration();
userConfig.fromBinary(UhkBuffer.fromArray(data));
if (userConfig.userConfigMajorVersion > 0) {
return userConfig;
}
return null;
}
private static getHardwareConfigFromDeviceResponse(json: string): HardwareConfiguration {
const data = JSON.parse(json);
const hardwareConfig = new HardwareConfiguration();
hardwareConfig.fromBinary(UhkBuffer.fromArray(data));
if (hardwareConfig.uniqueId > 0) {
return hardwareConfig;
}
return null;
}
@Effect() loadUserConfig$: Observable<Action> = defer(() => {
return Observable.of(new LoadUserConfigSuccessAction(this.getUserConfiguration()));
});
@@ -146,23 +128,24 @@ export class UserConfigEffects {
}
const result = [];
let newPageDestination = ['/'];
try {
const userConfig = UserConfigEffects.getUserConfigFromDeviceResponse(data.userConfiguration);
const userConfig = getUserConfigFromDeviceResponse(data.userConfiguration);
result.push(new LoadUserConfigSuccessAction(userConfig));
} catch (err) {
this.logService.error('Eeprom user-config parse error:', err);
result.push(
new ShowNotificationAction({
type: NotificationType.Error,
message: err
}));
const userConfig = new UserConfiguration().fromJsonObject(data.backupConfiguration);
result.push(new LoadUserConfigSuccessAction(this.getUserConfiguration()));
result.push(new HasBackupUserConfigurationAction(!!data.backupConfiguration));
result.push(new LoadUserConfigSuccessAction(userConfig));
newPageDestination = ['/device/restore-user-configuration'];
}
try {
const hardwareConfig = UserConfigEffects.getHardwareConfigFromDeviceResponse(data.hardwareConfiguration);
const hardwareConfig = getHardwareConfigFromDeviceResponse(data.hardwareConfiguration);
result.push(new LoadHardwareConfigurationSuccessAction(hardwareConfig));
} catch (err) {
this.logService.error('Eeprom hardware-config parse error:', err);
@@ -175,7 +158,7 @@ export class UserConfigEffects {
result.push(new HardwareModulesLoadedAction(data.modules));
this.router.navigate(['/']);
this.router.navigate(newPageDestination);
return result;
});

View File

@@ -39,7 +39,6 @@ export const metaReducers: MetaReducer<AppState>[] = environment.production
: [storeFreeze];
export const getUserConfiguration = (state: AppState) => state.userConfiguration;
export const getDeviceName = createSelector(getUserConfiguration, fromUserConfig.getDeviceName);
export const appState = (state: AppState) => state.app;
@@ -47,10 +46,10 @@ export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu);
export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification);
export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration);
export const runningInElectron = createSelector(appState, fromApp.runningInElectron);
export const getHardwareConfiguration = createSelector(appState, fromApp.getHardwareConfiguration);
export const getKeyboardLayout = createSelector(appState, fromApp.getKeyboardLayout);
export const deviceConfigurationLoaded = createSelector(appState, fromApp.deviceConfigurationLoaded);
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
export const getPrivilegePageState = createSelector(appState, fromApp.getPrivilagePageState);
export const appUpdateState = (state: AppState) => state.appUpdate;
export const getShowAppUpdateAvailable = createSelector(appUpdateState, fromAppUpdate.getShowAppUpdateAvailable);
@@ -78,3 +77,28 @@ export const firmwareOkButtonDisabled = createSelector(deviceState, fromDevice.f
// tslint:disable-next-line: max-line-length
export const flashFirmwareButtonDisbabled = createSelector(runningInElectron, deviceState, (electron, state: fromDevice.State) => !electron || state.updatingFirmware);
export const getHardwareModules = createSelector(deviceState, fromDevice.getHardwareModules);
export const getBackupUserConfigurationState = createSelector(deviceState, fromDevice.getBackupUserConfigurationState);
export const getRestoreUserConfiguration = createSelector(deviceState, fromDevice.getHasBackupUserConfiguration);
export const getSideMenuPageState = createSelector(
showAddonMenu,
runningInElectron,
updatingFirmware,
getUserConfiguration,
getRestoreUserConfiguration,
(showAddonMenuValue: boolean,
runningInElectronValue: boolean,
updatingFirmwareValue: boolean,
userConfiguration: UserConfiguration,
restoreUserConfiguration: boolean) => {
return {
showAddonMenu: showAddonMenuValue,
runInElectron: runningInElectronValue,
updatingFirmware: updatingFirmwareValue,
deviceName: userConfiguration.deviceName,
keymaps: userConfiguration.keymaps,
macros: userConfiguration.macros,
restoreUserConfiguration
};
}
);

View File

@@ -1,13 +1,20 @@
import { ROUTER_NAVIGATION } from '@ngrx/router-store';
import { Action } from '@ngrx/store';
import { VersionInformation } from 'uhk-common';
import {
HardwareConfiguration,
Notification,
NotificationType,
runInElectron,
UserConfiguration,
VersionInformation
} from 'uhk-common';
import { HardwareConfiguration, Notification, NotificationType, runInElectron, UserConfiguration } from 'uhk-common';
import { ActionTypes, ShowNotificationAction } from '../actions/app';
import { ActionTypes as UserConfigActionTypes } from '../actions/user-config';
import { ActionTypes as DeviceActionTypes } from '../actions/device';
import { KeyboardLayout } from '../../keyboard/keyboard-layout.enum';
import { getVersions } from '../../util';
import { PrivilagePageSate } from '../../models/privilage-page-sate';
export interface State {
started: boolean;
@@ -19,6 +26,8 @@ export interface State {
configLoading: boolean;
hardwareConfig?: HardwareConfiguration;
agentVersionInfo?: VersionInformation;
privilegeWhatWillThisDoClicked: boolean;
permissionError?: any;
}
export const initialState: State = {
@@ -27,7 +36,8 @@ export const initialState: State = {
navigationCountAfterNotification: 0,
runningInElectron: runInElectron(),
configLoading: true,
agentVersionInfo: getVersions()
agentVersionInfo: getVersions(),
privilegeWhatWillThisDoClicked: false
};
export function reducer(state = initialState, action: Action & { payload: any }) {
@@ -115,6 +125,24 @@ export function reducer(state = initialState, action: Action & { payload: any })
};
}
case ActionTypes.PRIVILEGE_WHAT_WILL_THIS_DO:
return {
...state,
privilegeWhatWillThisDoClicked: true
};
case ActionTypes.SETUP_PERMISSION_ERROR:
return {
...state,
permissionError: action.payload
};
case DeviceActionTypes.SET_PRIVILEGE_ON_LINUX:
return {
...state,
permissionError: null
};
default:
return state;
}
@@ -124,7 +152,6 @@ export const showAddonMenu = (state: State) => state.showAddonMenu;
export const getUndoableNotification = (state: State) => state.undoableNotification;
export const getPrevUserConfiguration = (state: State) => state.prevUserConfig;
export const runningInElectron = (state: State) => state.runningInElectron;
export const getHardwareConfiguration = (state: State) => state.hardwareConfig;
export const getKeyboardLayout = (state: State): KeyboardLayout => {
if (state.hardwareConfig && state.hardwareConfig.isIso) {
return KeyboardLayout.ISO;
@@ -134,3 +161,12 @@ export const getKeyboardLayout = (state: State): KeyboardLayout => {
};
export const deviceConfigurationLoaded = (state: State) => !state.runningInElectron ? true : !!state.hardwareConfig;
export const getAgentVersionInfo = (state: State) => state.agentVersionInfo || {} as VersionInformation;
export const getPrivilagePageState = (state: State): PrivilagePageSate => {
const permissionSetupFailed = !!state.permissionError;
return {
permissionSetupFailed,
showWhatWillThisDo: !state.privilegeWhatWillThisDoClicked && !permissionSetupFailed,
showWhatWillThisDoContent: state.privilegeWhatWillThisDoClicked || permissionSetupFailed
};
};

View File

@@ -6,11 +6,13 @@ import {
ConnectionStateChangedAction,
HardwareModulesLoadedAction,
SaveConfigurationAction,
HasBackupUserConfigurationAction,
UpdateFirmwareFailedAction
} from '../actions/device';
import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app';
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
import { XtermCssClass, XtermLog } from '../../models/xterm-log';
import { RestoreConfigurationState } from '../../models/restore-configuration-state';
export interface State {
connected: boolean;
@@ -20,6 +22,8 @@ export interface State {
firmwareUpdateFinished: boolean;
modules: HardwareModules;
log: Array<XtermLog>;
restoringUserConfiguration: boolean;
hasBackupUserConfiguration: boolean;
}
export const initialState: State = {
@@ -37,7 +41,9 @@ export const initialState: State = {
firmwareVersion: ''
}
},
log: [{message: '', cssClass: XtermCssClass.standard}]
log: [{message: '', cssClass: XtermCssClass.standard}],
restoringUserConfiguration: false,
hasBackupUserConfiguration: false
};
export function reducer(state = initialState, action: Action) {
@@ -87,7 +93,8 @@ export function reducer(state = initialState, action: Action) {
showButton: true,
text: 'Saved!',
action: null
}
},
restoringUserConfiguration: false
};
}
@@ -167,6 +174,25 @@ export function reducer(state = initialState, action: Action) {
modules: (action as HardwareModulesLoadedAction).payload
};
case ActionTypes.RESET_USER_CONFIGURATION:
case ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP:
return {
...state,
restoringUserConfiguration: true
};
case ActionTypes.HAS_BACKUP_USER_CONFIGURATION:
return {
...state,
hasBackupUserConfiguration: (action as HasBackupUserConfigurationAction).payload
};
case ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS:
return {
...state,
hasBackupUserConfiguration: false
};
default:
return state;
}
@@ -179,3 +205,10 @@ export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
export const xtermLog = (state: State) => state.log;
export const firmwareOkButtonDisabled = (state: State) => !state.firmwareUpdateFinished;
export const getHardwareModules = (state: State) => state.modules;
export const getHasBackupUserConfiguration = (state: State) => state.hasBackupUserConfiguration;
export const getBackupUserConfigurationState = (state: State): RestoreConfigurationState => {
return {
restoringUserConfiguration: state.restoringUserConfiguration,
hasBackupUserConfiguration: state.hasBackupUserConfiguration
};
};

View File

@@ -494,5 +494,3 @@ function setKeyActionToLayer(newLayer: Layer, moduleIndex: number, keyIndex: num
newModule.keyActions = newModule.keyActions.slice();
newModule.keyActions[keyIndex] = newKeyAction;
}
export const getDeviceName = (state: UserConfiguration) => state.deviceName;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -115,3 +115,43 @@ a.disabled {
display: block;
}
}
a.link-inline {
cursor: pointer;
}
@mixin code-style() {
color: #6a737d;
background-color: #f6f8fa;
text-align: left;
}
code {
@include code-style();
}
pre {
code {
@include code-style();
}
}
.mt-10 {
margin-top: 10px;
}
.copy-container {
position: relative;
.fa-copy {
cursor: pointer;
color: #6a737d;
position: absolute;
right: 4px;
top: 4px;
&:hover {
color: darken(#6a737d, 15);
}
}
}

View File

@@ -1,10 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="15px" height="15px" viewBox="0 0 15 15" enable-background="new 0 0 15 15" xml:space="preserve">
<path fill="#333333" d="M3,0C1.338,0,0,1.338,0,3v9c0,1.662,1.338,3,3,3h9c1.662,0,3-1.338,3-3V3c0-1.662-1.338-3-3-3H3z
M1.375,3.75c0.07-0.012,0.125,0,0.125,0H6c0.375,0,0.75,0.375,0.75,0.375S7.125,4.5,7.5,4.5s0.75-0.375,0.75-0.375
S8.625,3.75,9,3.75h4.5c0.75,0,0.75,0.75,0.75,0.75V6c0,2.25-1.5,2.25-1.5,2.25H9c-0.375,0-0.75-1.125-0.75-1.125S7.875,6,7.5,6
S6.75,7.125,6.75,7.125S6.375,8.25,6,8.25H2.25c0,0-1.5,0-1.5-2.25V4.5C0.75,3.938,1.164,3.785,1.375,3.75z"/>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
id="svg2"
version="1.1"
xml:space="preserve"
width="15"
height="15"
viewBox="0 0 15 15"><metadata
id="metadata20"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12,0,0,-12,0,6)"
spreadMethod="pad"
id="linearGradient5644"><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0"
id="stop5646" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop5648" /><stop
style="stop-opacity:1;stop-color:#5d5f63"
offset="0.00137794"
id="stop5650" /><stop
style="stop-opacity:1;stop-color:#cccccc"
offset="0.52557927"
id="stop5652" /><stop
style="stop-opacity:1;stop-color:#5d5c62"
offset="1"
id="stop5654" /></linearGradient><linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12,0,0,-12,0,6)"
spreadMethod="pad"
id="linearGradient4158-3"><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0"
id="stop5634" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop5636" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop5638" /><stop
style="stop-opacity:1;stop-color:#cccccc"
offset="0.49464694"
id="stop5640" /><stop
style="stop-opacity:1;stop-color:#5d5c62"
offset="1"
id="stop5642" /></linearGradient><linearGradient
osb:paint="solid"
id="linearGradient5618"><stop
id="stop5620"
offset="0"
style="stop-color:#cccccc;stop-opacity:1;" /></linearGradient><linearGradient
id="linearGradient4158"
spreadMethod="pad"
gradientTransform="matrix(12,0,0,-12,0,6)"
gradientUnits="userSpaceOnUse"
y2="0"
x2="1"
y1="0"
x1="0"><stop
id="stop4160"
offset="0"
style="stop-opacity:1;stop-color:#85878d" /><stop
id="stop4162"
offset="0.00137794"
style="stop-opacity:1;stop-color:#85878d" /><stop
id="stop4164"
offset="0.00137794"
style="stop-opacity:1;stop-color:#85878d" /><stop
id="stop4166"
offset="0.49464694"
style="stop-opacity:1;stop-color:#cccccc" /><stop
id="stop4168"
offset="1"
style="stop-opacity:1;stop-color:#5d5c62" /></linearGradient><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath18"><path
d="M 2.398,12 C 1.07,12 0,10.93 0,9.602 L 0,9.602 0,2.398 C 0,1.07 1.07,0 2.398,0 L 2.398,0 9.601,0 C 10.93,0 12,1.07 12,2.398 L 12,2.398 12,9.602 C 12,10.93 10.93,12 9.601,12 L 9.601,12 2.398,12 Z M 0.602,7.199 0.602,8.398 C 0.602,8.852 0.931,8.973 1.102,9 L 1.102,9 C 1.156,9.008 1.199,9 1.199,9 L 1.199,9 4.801,9 C 5.102,9 5.398,8.699 5.398,8.699 L 5.398,8.699 C 5.398,8.699 5.699,8.398 6,8.398 L 6,8.398 C 6.301,8.398 6.602,8.699 6.602,8.699 L 6.602,8.699 C 6.602,8.699 6.898,9 7.199,9 L 7.199,9 10.801,9 C 11.398,9 11.398,8.398 11.398,8.398 L 11.398,8.398 11.398,7.199 C 11.398,5.398 10.199,5.398 10.199,5.398 L 10.199,5.398 7.199,5.398 C 6.898,5.398 6.602,6.301 6.602,6.301 L 6.602,6.301 C 6.602,6.301 6.301,7.199 6,7.199 L 6,7.199 C 5.699,7.199 5.398,6.301 5.398,6.301 L 5.398,6.301 C 5.398,6.301 5.102,5.398 4.801,5.398 L 4.801,5.398 1.801,5.398 C 1.801,5.398 0.602,5.398 0.602,7.199"
id="path20" /></clipPath><linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12,0,0,-12,0,6)"
spreadMethod="pad"
id="linearGradient26"><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0"
id="stop28" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop30" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop32" /><stop
style="stop-opacity:1;stop-color:#b6b6b6"
offset="0.48571813"
id="stop34" /><stop
style="stop-opacity:1;stop-color:#5d5c62"
offset="1"
id="stop36" /></linearGradient><linearGradient
gradientUnits="userSpaceOnUse"
y2="0"
x2="11.745763"
y1="11.542373"
x1="0.91525424"
id="linearGradient4154"
xlink:href="#linearGradient4158" /><linearGradient
y2="-0.050847456"
x2="12"
y1="11.898305"
x1="0.10169491"
gradientUnits="userSpaceOnUse"
id="linearGradient4156"
xlink:href="#linearGradient5644" /></defs><g
id="g10"
transform="matrix(1.25,0,0,-1.25,0,15)"><path
d="M 11.473,5.117 0.416,5.117 0.416,9.383 11.473,9.383 11.473,5.117 Z"
style="fill:#343434;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path12" /><g
style="fill:url(#linearGradient4154);fill-opacity:1.0"
clip-path="url(#clipPath18)"
id="g16"><g
style="fill:url(#linearGradient4154);fill-opacity:1.0"
id="g22"><g
style="fill:url(#linearGradient4154);fill-opacity:1.0"
id="g24"><path
id="path38"
style="fill:url(#linearGradient4156);stroke:none;fill-opacity:1;stroke-opacity:1"
d="M 2.398,12 C 1.07,12 0,10.93 0,9.602 L 0,9.602 0,2.398 C 0,1.07 1.07,0 2.398,0 L 2.398,0 9.601,0 C 10.93,0 12,1.07 12,2.398 L 12,2.398 12,9.602 C 12,10.93 10.93,12 9.601,12 L 9.601,12 2.398,12 Z M 0.602,7.199 0.602,8.398 C 0.602,8.852 0.931,8.973 1.102,9 L 1.102,9 C 1.156,9.008 1.199,9 1.199,9 L 1.199,9 4.801,9 C 5.102,9 5.398,8.699 5.398,8.699 L 5.398,8.699 C 5.398,8.699 5.699,8.398 6,8.398 L 6,8.398 C 6.301,8.398 6.602,8.699 6.602,8.699 L 6.602,8.699 C 6.602,8.699 6.898,9 7.199,9 L 7.199,9 10.801,9 C 11.398,9 11.398,8.398 11.398,8.398 L 11.398,8.398 11.398,7.199 C 11.398,5.398 10.199,5.398 10.199,5.398 L 10.199,5.398 7.199,5.398 C 6.898,5.398 6.602,6.301 6.602,6.301 L 6.602,6.301 C 6.602,6.301 6.301,7.199 6,7.199 L 6,7.199 C 5.699,7.199 5.398,6.301 5.398,6.301 L 5.398,6.301 C 5.398,6.301 5.102,5.398 4.801,5.398 L 4.801,5.398 1.801,5.398 C 1.801,5.398 0.602,5.398 0.602,7.199" /></g></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 935 B

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -1,4 +1,149 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-5 -5 9.9999999 10" height="15" width="15">
<path style="fill:#333" d="M -3,-5 C -4.108,-5 -5,-4.108 -5,-3 L -5,3 C -5,4.108 -4.108,5 -3,5 L 3,5 C 4.108,5 5,4.108 5,3 L 5,-3 C 5,-4.108 4.108,-5 3,-5 L -3,-5 Z M -4.0833333,-2.5 C -4.0364583,-2.5078125 -4,-2.5 -4,-2.5 L -0.99999998,-2.5 C -0.74999998,-2.5 -0.49999998,-2.25 -0.49999998,-2.25 -0.49999998,-2.25 -0.24999998,-2 2.4999999e-8,-2 0.25000003,-2 0.50000003,-2.25 0.50000003,-2.25 0.50000003,-2.25 0.75000003,-2.5 1,-2.5 L 4,-2.5 C 4.5,-2.5 4.5,-2 4.5,-2 L 4.5,-0.99999998 C 4.5,0.50000003 3.5,0.50000003 3.5,0.50000003 L 1,0.50000003 C 0.75000003,0.50000003 0.50000003,-0.24999998 0.50000003,-0.24999998 0.50000003,-0.24999998 0.25000003,-0.99999998 2.4999999e-8,-0.99999998 -0.24999998,-0.99999998 -0.49999998,-0.24999998 -0.49999998,-0.24999998 -0.49999998,-0.24999998 -0.74999998,0.50000003 -0.99999998,0.50000003 L -3.5,0.50000003 C -3.5,0.50000003 -4.5,0.50000003 -4.5,-0.99999998 L -4.5,-2 C -4.5,-2.375 -4.2239583,-2.4765625 -4.0833333,-2.5 Z" />
</svg>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
id="svg2"
version="1.1"
xml:space="preserve"
width="15"
height="15"
viewBox="0 0 15 15"><metadata
id="metadata20"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12,0,0,-12,0,6)"
spreadMethod="pad"
id="linearGradient5644"><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0"
id="stop5646" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop5648" /><stop
style="stop-opacity:1;stop-color:#5d5f63"
offset="0.00137794"
id="stop5650" /><stop
style="stop-opacity:1;stop-color:#cccccc"
offset="0.52557927"
id="stop5652" /><stop
style="stop-opacity:1;stop-color:#5d5c62"
offset="1"
id="stop5654" /></linearGradient><linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12,0,0,-12,0,6)"
spreadMethod="pad"
id="linearGradient4158-3"><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0"
id="stop5634" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop5636" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop5638" /><stop
style="stop-opacity:1;stop-color:#cccccc"
offset="0.49464694"
id="stop5640" /><stop
style="stop-opacity:1;stop-color:#5d5c62"
offset="1"
id="stop5642" /></linearGradient><linearGradient
osb:paint="solid"
id="linearGradient5618"><stop
id="stop5620"
offset="0"
style="stop-color:#cccccc;stop-opacity:1;" /></linearGradient><linearGradient
id="linearGradient4158"
spreadMethod="pad"
gradientTransform="matrix(12,0,0,-12,0,6)"
gradientUnits="userSpaceOnUse"
y2="0"
x2="1"
y1="0"
x1="0"><stop
id="stop4160"
offset="0"
style="stop-opacity:1;stop-color:#85878d" /><stop
id="stop4162"
offset="0.00137794"
style="stop-opacity:1;stop-color:#85878d" /><stop
id="stop4164"
offset="0.00137794"
style="stop-opacity:1;stop-color:#85878d" /><stop
id="stop4166"
offset="0.49464694"
style="stop-opacity:1;stop-color:#cccccc" /><stop
id="stop4168"
offset="1"
style="stop-opacity:1;stop-color:#5d5c62" /></linearGradient><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath18"><path
d="M 2.398,12 C 1.07,12 0,10.93 0,9.602 L 0,9.602 0,2.398 C 0,1.07 1.07,0 2.398,0 L 2.398,0 9.601,0 C 10.93,0 12,1.07 12,2.398 L 12,2.398 12,9.602 C 12,10.93 10.93,12 9.601,12 L 9.601,12 2.398,12 Z M 0.602,7.199 0.602,8.398 C 0.602,8.852 0.931,8.973 1.102,9 L 1.102,9 C 1.156,9.008 1.199,9 1.199,9 L 1.199,9 4.801,9 C 5.102,9 5.398,8.699 5.398,8.699 L 5.398,8.699 C 5.398,8.699 5.699,8.398 6,8.398 L 6,8.398 C 6.301,8.398 6.602,8.699 6.602,8.699 L 6.602,8.699 C 6.602,8.699 6.898,9 7.199,9 L 7.199,9 10.801,9 C 11.398,9 11.398,8.398 11.398,8.398 L 11.398,8.398 11.398,7.199 C 11.398,5.398 10.199,5.398 10.199,5.398 L 10.199,5.398 7.199,5.398 C 6.898,5.398 6.602,6.301 6.602,6.301 L 6.602,6.301 C 6.602,6.301 6.301,7.199 6,7.199 L 6,7.199 C 5.699,7.199 5.398,6.301 5.398,6.301 L 5.398,6.301 C 5.398,6.301 5.102,5.398 4.801,5.398 L 4.801,5.398 1.801,5.398 C 1.801,5.398 0.602,5.398 0.602,7.199"
id="path20" /></clipPath><linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(12,0,0,-12,0,6)"
spreadMethod="pad"
id="linearGradient26"><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0"
id="stop28" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop30" /><stop
style="stop-opacity:1;stop-color:#85878d"
offset="0.00137794"
id="stop32" /><stop
style="stop-opacity:1;stop-color:#b6b6b6"
offset="0.48571813"
id="stop34" /><stop
style="stop-opacity:1;stop-color:#5d5c62"
offset="1"
id="stop36" /></linearGradient><linearGradient
gradientUnits="userSpaceOnUse"
y2="0"
x2="11.745763"
y1="11.542373"
x1="0.91525424"
id="linearGradient4154"
xlink:href="#linearGradient4158" /><linearGradient
y2="-0.050847456"
x2="12"
y1="11.898305"
x1="0.10169491"
gradientUnits="userSpaceOnUse"
id="linearGradient4156"
xlink:href="#linearGradient5644" /></defs><g
id="g10"
transform="matrix(1.25,0,0,-1.25,0,15)"><path
d="M 11.473,5.117 0.416,5.117 0.416,9.383 11.473,9.383 11.473,5.117 Z"
style="fill:#343434;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path12" /><g
style="fill:url(#linearGradient4154);fill-opacity:1.0"
clip-path="url(#clipPath18)"
id="g16"><g
style="fill:url(#linearGradient4154);fill-opacity:1.0"
id="g22"><g
style="fill:url(#linearGradient4154);fill-opacity:1.0"
id="g24"><path
id="path38"
style="fill:url(#linearGradient4156);stroke:none;fill-opacity:1;stroke-opacity:1"
d="M 2.398,12 C 1.07,12 0,10.93 0,9.602 L 0,9.602 0,2.398 C 0,1.07 1.07,0 2.398,0 L 2.398,0 9.601,0 C 10.93,0 12,1.07 12,2.398 L 12,2.398 12,9.602 C 12,10.93 10.93,12 9.601,12 L 9.601,12 2.398,12 Z M 0.602,7.199 0.602,8.398 C 0.602,8.852 0.931,8.973 1.102,9 L 1.102,9 C 1.156,9.008 1.199,9 1.199,9 L 1.199,9 4.801,9 C 5.102,9 5.398,8.699 5.398,8.699 L 5.398,8.699 C 5.398,8.699 5.699,8.398 6,8.398 L 6,8.398 C 6.301,8.398 6.602,8.699 6.602,8.699 L 6.602,8.699 C 6.602,8.699 6.898,9 7.199,9 L 7.199,9 10.801,9 C 11.398,9 11.398,8.398 11.398,8.398 L 11.398,8.398 11.398,7.199 C 11.398,5.398 10.199,5.398 10.199,5.398 L 10.199,5.398 7.199,5.398 C 6.898,5.398 6.602,6.301 6.602,6.301 L 6.602,6.301 C 6.602,6.301 6.301,7.199 6,7.199 L 6,7.199 C 5.699,7.199 5.398,6.301 5.398,6.301 L 5.398,6.301 C 5.398,6.301 5.102,5.398 4.801,5.398 L 4.801,5.398 1.801,5.398 C 1.801,5.398 0.602,5.398 0.602,7.199" /></g></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -11,4 +11,24 @@ const firmwareMajorVersion = uhk.getUint16(response, 1);
const firmwareMinorVersion = uhk.getUint16(response, 3);
const firmwarePatchVersion = uhk.getUint16(response, 5);
const deviceProtocolMajorVersion = uhk.getUint16(response, 7);
const deviceProtocolMinorVersion = uhk.getUint16(response, 9);
const deviceProtocolPatchVersion = uhk.getUint16(response, 11);
const moduleProtocolMajorVersion = uhk.getUint16(response, 13);
const moduleProtocolMinorVersion = uhk.getUint16(response, 15);
const moduleProtocolPatchVersion = uhk.getUint16(response, 17);
const userConfigMajorVersion = uhk.getUint16(response, 19);
const userConfigMinorVersion = uhk.getUint16(response, 21);
const userConfigPatchVersion = uhk.getUint16(response, 23);
const hardwareConfigMajorVersion = uhk.getUint16(response, 25);
const hardwareConfigMinorVersion = uhk.getUint16(response, 27);
const hardwareConfigPatchVersion = uhk.getUint16(response, 29);
console.log(`firmwareVersion: ${firmwareMajorVersion}.${firmwareMinorVersion}.${firmwarePatchVersion}`);
console.log(`deviceProtocolVersion: ${deviceProtocolMajorVersion}.${deviceProtocolMinorVersion}.${deviceProtocolPatchVersion}`);
console.log(`moduleProtocolVersion: ${moduleProtocolMajorVersion}.${moduleProtocolMinorVersion}.${moduleProtocolPatchVersion}`);
console.log(`userConfigVersion: ${userConfigMajorVersion}.${userConfigMinorVersion}.${userConfigPatchVersion}`);
console.log(`hardwareConfigVersion: ${hardwareConfigMajorVersion}.${hardwareConfigMinorVersion}.${hardwareConfigPatchVersion}`);

View File

@@ -0,0 +1 @@
@#%#@^^@#^@#$invalid config@#$@^%@^@@%

View File

@@ -13,11 +13,11 @@
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "1.9.0"
"color-convert": "1.9.1"
}
},
"aproba": {
@@ -31,7 +31,7 @@
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"requires": {
"delegates": "1.0.0",
"readable-stream": "2.3.3"
"readable-stream": "2.3.6"
}
},
"balanced-match": {
@@ -50,17 +50,18 @@
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw=="
},
"bl": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
"integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
"readable-stream": "2.3.3"
"readable-stream": "2.3.6",
"safe-buffer": "5.1.1"
}
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
@@ -72,7 +73,7 @@
"integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=",
"requires": {
"base64-js": "0.0.8",
"ieee754": "1.1.8",
"ieee754": "1.1.11",
"isarray": "1.0.0"
}
},
@@ -86,9 +87,9 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
"integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
"requires": {
"ansi-styles": "3.2.0",
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
"supports-color": "4.4.0"
"supports-color": "4.5.0"
}
},
"chownr": {
@@ -102,9 +103,9 @@
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"color-convert": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
"integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
"requires": {
"color-name": "1.1.3"
}
@@ -115,9 +116,9 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"commander": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ=="
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
},
"concat-map": {
"version": "0.0.1",
@@ -144,11 +145,19 @@
"decompress-targz": "4.1.1",
"decompress-unzip": "4.0.1",
"graceful-fs": "4.1.11",
"make-dir": "1.1.0",
"make-dir": "1.2.0",
"pify": "2.3.0",
"strip-dirs": "2.1.0"
}
},
"decompress-response": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
"integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
"requires": {
"mimic-response": "1.0.0"
}
},
"decompress-tar": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz",
@@ -156,7 +165,7 @@
"requires": {
"file-type": "5.2.0",
"is-stream": "1.1.0",
"tar-stream": "1.5.4"
"tar-stream": "1.5.5"
}
},
"decompress-tarbz2": {
@@ -216,10 +225,15 @@
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"end-of-stream": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz",
"integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"requires": {
"once": "1.4.0"
}
@@ -320,9 +334,9 @@
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"ieee754": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz",
"integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg=="
},
"inflight": {
"version": "1.0.6",
@@ -339,14 +353,14 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
"integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4="
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"interpret": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz",
"integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA="
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
"integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
@@ -372,9 +386,9 @@
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"make-dir": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz",
"integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz",
"integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==",
"requires": {
"pify": "3.0.0"
},
@@ -386,12 +400,17 @@
}
}
},
"mimic-response": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz",
"integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "1.1.8"
"brace-expansion": "1.1.11"
}
},
"minimist": {
@@ -415,14 +434,17 @@
}
},
"nan": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz",
"integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U="
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
},
"node-abi": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.1.1.tgz",
"integrity": "sha512-6oxV13poCOv7TfGvhsSz6XZWpXeKkdGVh72++cs33OfMh3KAX8lN84dCvmqSETyDXAFcUHtV7eJrgFBoOqZbNQ=="
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.3.0.tgz",
"integrity": "sha512-zwm6vU3SsVgw3e9fu48JBaRBCJGIvAgysDsqtf5+vEexFE71bEOtaMWb5zr/zODZNzTPtQlqUUpC79k68Hspow==",
"requires": {
"semver": "5.5.0"
}
},
"node-hid": {
"version": "0.5.7",
@@ -430,8 +452,8 @@
"integrity": "sha512-dwwpOetL2+MGYgivbO22ML+45ieCGbueWv1rYxRgBoEc2QMp6UF6ZucEkYts1IA3YPWJNkmpGh6dqQ85n19szw==",
"requires": {
"bindings": "1.3.0",
"nan": "2.6.2",
"prebuild-install": "2.3.0"
"nan": "2.10.0",
"prebuild-install": "2.5.1"
}
},
"noop-logger": {
@@ -512,62 +534,63 @@
}
},
"prebuild-install": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.3.0.tgz",
"integrity": "sha512-gzjq2oHB8oMbzJSsSh9MQ64zrXZGt092/uT4TLZlz2qnrPxpWqp4vYB7LZrDxnlxf5RfbCjkgDI/z0EIVuYzAw==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.1.tgz",
"integrity": "sha512-3DX9L6pzwc1m1ksMkW3Ky2WLgPQUBiySOfXVl3WZyAeJSyJb4wtoH9OmeRGcubAWsMlLiL8BTHbwfm/jPQE9Ag==",
"requires": {
"detect-libc": "1.0.3",
"expand-template": "1.1.0",
"github-from-package": "0.0.0",
"minimist": "1.2.0",
"mkdirp": "0.5.1",
"node-abi": "2.1.1",
"node-abi": "2.3.0",
"noop-logger": "0.1.1",
"npmlog": "4.1.2",
"os-homedir": "1.0.2",
"pump": "1.0.2",
"rc": "1.2.2",
"simple-get": "1.4.3",
"pump": "2.0.1",
"rc": "1.2.6",
"simple-get": "2.7.0",
"tar-fs": "1.16.0",
"tunnel-agent": "0.6.0",
"xtend": "4.0.1"
"which-pm-runs": "1.0.0"
}
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"pump": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz",
"integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE=",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
"requires": {
"end-of-stream": "1.4.0",
"end-of-stream": "1.4.1",
"once": "1.4.0"
}
},
"rc": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz",
"integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz",
"integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=",
"requires": {
"deep-extend": "0.4.2",
"ini": "1.3.4",
"ini": "1.3.5",
"minimist": "1.2.0",
"strip-json-comments": "2.0.1"
}
},
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"process-nextick-args": "2.0.0",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"string_decoder": "1.1.1",
"util-deprecate": "1.0.2"
}
},
@@ -576,13 +599,13 @@
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
"integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
"requires": {
"resolve": "1.5.0"
"resolve": "1.7.0"
}
},
"resolve": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
"integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==",
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.0.tgz",
"integrity": "sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA==",
"requires": {
"path-parse": "1.0.5"
}
@@ -610,6 +633,11 @@
}
}
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -621,7 +649,7 @@
"integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=",
"requires": {
"glob": "7.1.2",
"interpret": "1.0.4",
"interpret": "1.1.0",
"rechoir": "0.6.2"
}
},
@@ -640,14 +668,19 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"simple-concat": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
},
"simple-get": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-1.4.3.tgz",
"integrity": "sha1-6XVe2kB+ltpAxeUVjJ6jezO+y+s=",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.7.0.tgz",
"integrity": "sha512-RkE9rGPHcxYZ/baYmgJtOSM63vH0Vyq+ma5TijBcLla41SWlh8t6XYIGMR/oeZcmr+/G8k+zrClkkVrtnQ0esg==",
"requires": {
"decompress-response": "3.3.0",
"once": "1.4.0",
"unzip-response": "1.0.2",
"xtend": "4.0.1"
"simple-concat": "1.0.0"
}
},
"string-width": {
@@ -661,9 +694,9 @@
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "5.1.1"
}
@@ -690,9 +723,9 @@
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"supports-color": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
"integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"requires": {
"has-flag": "2.0.0"
}
@@ -704,18 +737,29 @@
"requires": {
"chownr": "1.0.1",
"mkdirp": "0.5.1",
"pump": "1.0.2",
"tar-stream": "1.5.4"
"pump": "1.0.3",
"tar-stream": "1.5.5"
},
"dependencies": {
"pump": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
"integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
"requires": {
"end-of-stream": "1.4.1",
"once": "1.4.0"
}
}
}
},
"tar-stream": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz",
"integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=",
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz",
"integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==",
"requires": {
"bl": "1.2.1",
"end-of-stream": "1.4.0",
"readable-stream": "2.3.3",
"bl": "1.2.2",
"end-of-stream": "1.4.1",
"readable-stream": "2.3.6",
"xtend": "4.0.1"
}
},
@@ -741,9 +785,9 @@
}
},
"typescript": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz",
"integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w=="
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz",
"integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q="
},
"unbzip2-stream": {
"version": "1.2.5",
@@ -754,16 +798,16 @@
"through": "2.3.8"
}
},
"unzip-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz",
"integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"which-pm-runs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
},
"wide-align": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",

View File

@@ -7,7 +7,7 @@
"devDependencies": {
"@types/node": "8.0.28",
"shx": "0.2.2",
"typescript": "2.5.3"
"typescript": "2.6.2"
},
"dependencies": {
"chalk": "2.1.0",

View File

@@ -71,7 +71,10 @@ function writeDevice(device, data, options={}) {
function getUhkDevice() {
const foundDevice = HID.devices().find(device =>
device.vendorId === 0x1d50 && device.productId === 0x6122 &&
((device.usagePage === 128 && device.usage === 129) || device.interface === 0));
// hidapi can not read the interface number on Mac, so check the usage page and usage
((device.usagePage === 128 && device.usage === 129) || // Old firmware
(device.usagePage === (0xFF00 | 0x00) && device.usage === 0x01) || // New firmware
device.interface === 0));
if (!foundDevice) {
return null;

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env node
const fs = require('fs');
const program = require('commander');
const uhk = require('./uhk');
(async function() {
program
.usage(`configPath`)
.parse(process.argv);
if (program.args.length < 1) {
console.error('No configPath path specified.');
exit(1);
}
const configPath = program.args[0];
const device = uhk.getUhkDevice();
const configBuffer = fs.readFileSync(configPath);
await uhk.writeConfig(device, configBuffer, false);
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, uhk.configBufferIds.stagingUserConfig);
})();

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env ts-node
///<reference path="./node_modules/@types/node/index.d.ts"/>
import { UhkBlhost, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { LogService } from 'uhk-common';
import * as fs from 'fs';
if (process.argv.length < 3) {
console.log(`use: write-userconfig <path to config file.bin>`);
process.exit(1);
}
const fileContent = fs.readFileSync(process.argv[2]);
const json = JSON.stringify(fileContent);
const logger = new LogService();
const uhkDevice = new UhkHidDevice(logger);
const uhkBlHost = new UhkBlhost(logger, '.');
const uhkOperations = new UhkOperations(logger, uhkBlHost, uhkDevice, '.');
const init = async (): Promise<void> => {
await uhkOperations.saveUserConfiguration(json);
};
init()
.then(() => {
console.log('Success');
})
.catch(error => {
console.log(error);
process.exit(-1);
});

View File

@@ -2,7 +2,7 @@
# apt-get install inkscape imagemagick-6.q16 icnsutils
agentIconSvg=../packages/uhk-web/src/svgs/keyboard/icons/agent-icon.svg
agentIconSvg=../packages/uhk-web/src/svgs/icons/agent-icon.svg
for size in 16 24 32 48 64 96 128 256 512 1024; do
inkscape -z --export-png=../build/icons/${size}x${size}.png -w $size $agentIconSvg