42 Commits

Author SHA1 Message Date
Róbert Kiss
b53751b408 chore(kboot): add more logging (#1008)
* chore(kboot): add more logging

* fix: add uncaughtException handler

* fix: wait to prevent race condition

* fix: don't close device after success left keyboard connection

* revert: remove extra delay

* revert: add back the waiting

* fix: always initialize new KBoot instance when try to configure I2C

* fix: increase the wait time between 2 IC2 reconnection

* fix: timing and usb reconnection

* fix: dont close kboot device

* feat: append the WithKboot to the firmware upgrade methods

* feat: revert back the blhost functionality
2019-09-13 10:17:44 +02:00
Róbert Kiss
8a7f30dbb1 chore: upgrade angular => 8.2.6 and cli => 8.3.4 (#1038) 2019-09-12 21:02:45 +02:00
László Monda
3b67e4d71d Update README.md 2019-09-11 22:16:51 +02:00
Róbert Kiss
d0626405a9 fix: es6 module loading (#1036)
The modification helps to handle correctly the ES6 default exports.
The firmware upgrade run correctly in the electron version and
kboot-firmware-upgrade.ts script
2019-09-09 22:14:34 +02:00
Róbert Kiss
df040fb78e chore: upgrade electron-builder => 20.44.4 and updater => 4.1.2 (#1035) 2019-09-09 20:31:12 +02:00
Róbert Kiss
ea4ca7c39c chore: upgrade npm-run-all => 4.1.5 (#1034) 2019-09-07 08:43:29 +02:00
Róbert Kiss
c477f9bcdc chore: upgrade check-node-version => 4.0.1 (#1033) 2019-09-06 20:48:20 +02:00
Róbert Kiss
abe740cf61 chore: remove old webpack dependencies (#1032) 2019-09-04 20:58:39 +02:00
Róbert Kiss
1003e7b14b chore: upgrade jsonfile => 5.0.0 (#1031) 2019-09-04 19:19:28 +02:00
Róbert Kiss
fef24613e4 feat: show macro usage count (#1030)
* feat: show macro usage count

* feat: show macro reference count only when ALT key hold down

* fix: the macro reference help text
2019-09-03 21:42:27 +02:00
László Monda
9844645409 Upgrade to firmware 8.6.0 2019-08-29 23:22:20 +02:00
László Monda
6814c8e126 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-25 13:48:10 +02:00
Róbert Kiss
d87d770042 chore: rebuild the root package-lock.json to refresh deps of deps (#1029) 2019-08-25 13:46:50 +02:00
László Monda
5dc2e6f47b Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-24 16:18:56 +02:00
Róbert Kiss
9f8926e34b chore: rebuild the uhk-web package-lock.json to refresh deps of deps (#1028) 2019-08-24 14:03:58 +02:00
Róbert Kiss
0bd1152a32 chore: upgrade copyfiles => 2.1.1 (#1027) 2019-08-23 23:19:00 +02:00
Róbert Kiss
4a1e88ed83 chore: upgrade angular => 8.2.3 (#1026) 2019-08-23 22:48:43 +02:00
Róbert Kiss
9db719a54b chore: upgrade electron-rebuild => 1.8.6 (#1025) 2019-08-23 22:11:11 +02:00
dgyimesi
49d31f90f7 feat: support more mouse buttons (#936)
* feat: support more mouse buttons (#834)

* Remove unused style.

* Retrigger CI

* fix: more button click and texts

* fix: the mouse button 4-8 naming

* feat: allow extra mouse buttons if userConfig version >= 4.1.1

* fix: version comparison

* fix: read correctly the right module informations
2019-08-23 22:00:01 +02:00
László Monda
d364ac85a6 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-23 21:32:39 +02:00
Róbert Kiss
fae83a4148 chore: upgrade @types/decompress => 4.2.3 (#1023) 2019-08-23 20:30:51 +02:00
Róbert Kiss
ac76674469 chore: add convert-user-config-to-bin npm script (#1022)
usage of the script
`$ npm run convert-user-config-to-bin -- <path of the bin file>`
2019-08-23 19:33:14 +02:00
László Monda
6867ef45f6 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-21 21:18:51 +02:00
Róbert Kiss
239c989cbe feat: Make the UHK of the web build split instead of merged (#1021) 2019-08-21 21:15:32 +02:00
László Monda
bfa8343aa5 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-21 21:03:09 +02:00
Róbert Kiss
0914a1496b fix: confirmation popover titles (#1019) 2019-08-20 16:02:29 +02:00
László Monda
a8c2866f95 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-20 15:39:52 +02:00
Róbert Kiss
79f467603a feat: scroll to the end of the firmware log when success of faild panel visible (#1018) 2019-08-20 15:33:28 +02:00
Róbert Kiss
c4269d4cf1 chore: use preserveWhitespaces = true in electron renderer (#1017)
The `npm run server:electron` build result much more simmilar to the production build
2019-08-20 15:19:00 +02:00
László Monda
d15e08430f Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-20 13:21:16 +02:00
Róbert Kiss
ee28065046 chore: upgrade angular => 8.2.2 (#996)
* chore: upgrade angular => 8.2.2

Summary:
- upgrade angular => 8.2.2
- setup the { static: false/true } for the ViewChild
- use alignment-baseline="middle" in svg text to align correct the texts

* fix: keymap popover crash
2019-08-20 12:54:23 +02:00
László Monda
a64a44fe41 Update NPM_UPDATES.md 2019-08-18 22:52:09 +02:00
László Monda
fa5f5cdc5d Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-18 21:59:16 +02:00
Róbert Kiss
4147243565 chore: upgrade electron => 5.0.9 (#1014)
Summary:
- Chromium: 73.0.3683.119
- Node.js: 12.0.0
- V8 engine: 7.3.492.27
2019-08-18 13:27:20 +02:00
Róbert Kiss
905007a597 chore: upgrade node-hid => 0.7.9 (#1011) 2019-08-14 17:10:39 +02:00
László Monda
c3fec647b6 Update ISSUE_TEMPLATE 2019-08-14 17:06:53 +02:00
László Monda
5f5aff71a5 Create NPM_UPDATES.md 2019-08-14 17:04:19 +02:00
László Monda
f65bf80c74 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-03 12:51:19 +02:00
László Monda
b691f866c5 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-01 23:48:43 +02:00
László Monda
4e09f95653 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-07-30 22:24:00 +02:00
László Monda
37a67805ce Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-07-28 21:34:55 +02:00
László Monda
735aae03d9 Update changelog. 2019-07-28 21:10:47 +02:00
100 changed files with 10637 additions and 12917 deletions

2
.nvmrc
View File

@@ -1 +1 @@
10.11.0 12.0.0

View File

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

View File

@@ -6,3 +6,5 @@ Before submitting a new issue, make sure to do the following:
3. Use Agent to update to the latest firmware: 3. Use Agent to update to the latest firmware:
https://github.com/UltimateHackingKeyboard/firmware/releases/latest https://github.com/UltimateHackingKeyboard/firmware/releases/latest
4. Try to reproduce the issue, and only report it if it still persists. 4. Try to reproduce the issue, and only report it if it still persists.
`npm audit` related issues will be closed due to https://github.com/UltimateHackingKeyboard/agent/blob/master/NPM_UPDATES.md

8
NPM_UPDATES.md Normal file
View File

@@ -0,0 +1,8 @@
We get requests from time to time to update our NPM dependencies because they contain vulnerabilities according to `npm audit`. Such issues will be closed without further consideration due to the following reasons:
1. Usually, the affected packages are not runtime dependencies of Agent, but devDependencies which are only needed for developing Agent.
2. Often times, 3rd party packages are affected by vulnerabilities which we cannot fix.
3. We can't just blindly update all of the packages because that'd likely break Agent as it has happened in the past. Each of the updates must be carefully tested, and we don't have the manpower to do it on a daily basis.
4. Sometimes `npm audit` signals false vulnerabilities.
We routinely update our dependencies on a best effort basis.

View File

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

View File

@@ -7,7 +7,7 @@ environment:
secure: 3IebpEKmC39codi1wT6dXx8mql4/mCL1JzZ7lir7GQ5MWRnCxlED2OXbiKHHigDV secure: 3IebpEKmC39codi1wT6dXx8mql4/mCL1JzZ7lir7GQ5MWRnCxlED2OXbiKHHigDV
CSC_LINK: c:\projects\uhk-agent\scripts\certs\windows-cert.p12 CSC_LINK: c:\projects\uhk-agent\scripts\certs\windows-cert.p12
matrix: matrix:
- nodejs_version: "10.11.0" - nodejs_version: "12.0.0"
cache: cache:
- node_modules -> package.json - node_modules -> package.json

11807
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,9 @@
"author": "Ultimate Gadget Laboratories", "author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js", "main": "electron/dist/electron-main.js",
"version": "1.2.13", "version": "1.2.13",
"firmwareVersion": "8.5.4", "firmwareVersion": "8.6.0",
"deviceProtocolVersion": "4.4.0", "deviceProtocolVersion": "4.5.0",
"userConfigVersion": "4.0.1", "userConfigVersion": "4.1.1",
"hardwareConfigVersion": "1.0.0", "hardwareConfigVersion": "1.0.0",
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.", "description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
"repository": { "repository": {
@@ -15,11 +15,11 @@
}, },
"license": "GPL-3.0", "license": "GPL-3.0",
"engines": { "engines": {
"node": ">=10.2.1 <11.0.0", "node": ">=12.0.0 <13.0.0",
"npm": ">=6.4.0 <7.0.0" "npm": ">=6.9.0 <7.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/decompress": "4.2.0", "@types/decompress": "4.2.3",
"@types/electron-devtools-installer": "2.0.2", "@types/electron-devtools-installer": "2.0.2",
"@types/electron-settings": "3.0.0", "@types/electron-settings": "3.0.0",
"@types/file-saver": "0.0.1", "@types/file-saver": "0.0.1",
@@ -27,47 +27,42 @@
"@types/jasmine": "3.3.12", "@types/jasmine": "3.3.12",
"@types/jasminewd2": "2.0.3", "@types/jasminewd2": "2.0.3",
"@types/jquery": "3.3.29", "@types/jquery": "3.3.29",
"@types/jsonfile": "4.0.1", "@types/jsonfile": "5.0.0",
"@types/lodash": "4.14.136", "@types/lodash": "4.14.136",
"@types/node": "8.0.53", "@types/node": "8.0.53",
"@types/node-hid": "0.7.0", "@types/node-hid": "0.7.2",
"@types/request": "2.0.8", "@types/request": "2.0.8",
"@types/semver": "5.5.0", "@types/semver": "5.5.0",
"@types/tmp": "0.0.33", "@types/tmp": "0.0.33",
"autoprefixer": "6.5.3",
"buffer": "5.0.6", "buffer": "5.0.6",
"check-node-version": "^3.2.0", "check-node-version": "4.0.1",
"copy-webpack-plugin": "5.0.0", "copy-webpack-plugin": "5.0.0",
"copyfiles": "^2.0.0", "copyfiles": "2.1.1",
"core-js": "2.4.1", "core-js": "2.4.1",
"cross-env": "5.0.5", "cross-env": "5.0.5",
"decompress": "4.2.0", "decompress": "4.2.0",
"decompress-tarbz2": "4.1.1", "decompress-tarbz2": "4.1.1",
"devtron": "1.4.0", "devtron": "1.4.0",
"electron": "4.2.8", "electron": "5.0.9",
"electron-builder": "20.34.0", "electron-builder": "20.44.4",
"electron-debug": "1.5.0", "electron-debug": "1.5.0",
"electron-devtools-installer": "2.2.3", "electron-devtools-installer": "2.2.3",
"electron-log": "2.2.16", "electron-log": "2.2.16",
"electron-rebuild": "1.8.5", "electron-rebuild": "1.8.6",
"electron-settings": "3.1.4", "electron-settings": "3.1.4",
"electron-updater": "2.21.4", "electron-updater": "4.1.2",
"exports-loader": "0.6.3",
"file-loader": "0.10.0",
"fs-extra": "8.1.0", "fs-extra": "8.1.0",
"gh-pages": "2.0.1", "gh-pages": "2.0.1",
"html-webpack-plugin": "3.2.0",
"jasmine": "3.4.0", "jasmine": "3.4.0",
"jasmine-core": "3.4.0", "jasmine-core": "3.4.0",
"jasmine-node": "3.0.0", "jasmine-node": "3.0.0",
"jasmine-ts": "0.3.0", "jasmine-ts": "0.3.0",
"jsonfile": "4.0.0", "jsonfile": "5.0.0",
"lerna": "3.16.4", "lerna": "3.16.4",
"lodash": "4.17.15", "lodash": "4.17.15",
"node-hid": "0.7.8", "node-hid": "0.7.9",
"npm-run-all": "4.0.2", "npm-run-all": "4.1.5",
"nrf-intel-hex": "1.3.0", "nrf-intel-hex": "1.3.0",
"postcss-url": "8.0.0",
"pre-commit": "1.2.2", "pre-commit": "1.2.2",
"request": "2.88.0", "request": "2.88.0",
"rimraf": "2.6.1", "rimraf": "2.6.1",
@@ -96,11 +91,13 @@
"server:electron": "lerna exec --scope uhk-web npm run server:renderer", "server:electron": "lerna exec --scope uhk-web npm run server:renderer",
"electron": "lerna exec --scope uhk-agent npm start", "electron": "lerna exec --scope uhk-agent npm start",
"electron:spe": "lerna exec --scope uhk-agent npm run electron:spe", "electron:spe": "lerna exec --scope uhk-agent npm run electron:spe",
"electron:kboot": "lerna exec --scope uhk-agent npm run electron:kboot",
"pack": "node ./scripts/release.js", "pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites", "sprites": "node ./scripts/generate-svg-sprites",
"release": "node ./scripts/release.js", "release": "node ./scripts/release.js",
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist ./tmp", "clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist ./tmp",
"predeploy-gh-pages": "lerna run build:web --scope=uhk-web", "predeploy-gh-pages": "lerna run build:web --scope=uhk-web",
"deploy-gh-pages": "gh-pages -d packages/uhk-web/dist" "deploy-gh-pages": "gh-pages -d packages/uhk-web/dist",
"convert-user-config-to-bin": "node -r ts-node/register ./packages/usb/user-config-json-to-bin.ts"
} }
} }

View File

@@ -24,13 +24,16 @@
} }
}, },
"bindings": { "bindings": {
"version": "1.3.1", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew==" "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"requires": {
"file-uri-to-path": "1.0.0"
}
}, },
"bl": { "bl": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": { "requires": {
"readable-stream": "^2.3.5", "readable-stream": "^2.3.5",
@@ -69,9 +72,9 @@
} }
}, },
"chownr": { "chownr": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
@@ -137,6 +140,11 @@
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
}, },
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"fs-constants": { "fs-constants": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -173,9 +181,9 @@
"integrity": "sha512-dDlJhYk8BAmH1HDncTjCt6xOm2+kT+MxGhRKB+mUoF8nocDzPAgZPEWTRI9QgkGvbDkbJgCqyxweGlIV0yhbUQ==" "integrity": "sha512-dDlJhYk8BAmH1HDncTjCt6xOm2+kT+MxGhRKB+mUoF8nocDzPAgZPEWTRI9QgkGvbDkbJgCqyxweGlIV0yhbUQ=="
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -202,12 +210,12 @@
}, },
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
@@ -215,7 +223,7 @@
"dependencies": { "dependencies": {
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
} }
} }
@@ -226,9 +234,9 @@
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}, },
"nan": { "nan": {
"version": "2.12.1", "version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
}, },
"napi-build-utils": { "napi-build-utils": {
"version": "1.0.1", "version": "1.0.1",
@@ -236,21 +244,21 @@
"integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==" "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA=="
}, },
"node-abi": { "node-abi": {
"version": "2.5.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.5.1.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.11.0.tgz",
"integrity": "sha512-oDbFc7vCFx0RWWCweTer3hFm1u+e60N5FtGnmRV6QqvgATGFH/XRR6vqWIeBVosCYCqt6YdIr2L0exLZuEdVcQ==", "integrity": "sha512-kuy/aEg75u40v378WRllQ4ZexaXJiCvB68D2scDXclp/I4cRq6togpbOoKhmN07tns9Zldu51NNERo0wehfX9g==",
"requires": { "requires": {
"semver": "^5.4.1" "semver": "^5.4.1"
} }
}, },
"node-hid": { "node-hid": {
"version": "0.7.4", "version": "0.7.9",
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.4.tgz", "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.9.tgz",
"integrity": "sha512-gvgNDPoszObn7avIDYMUvVv1T0xQB4/CZFJWckra/LXAc0qHYho4M1LCnCKlLIocL2R5/3qGv0J4AjRMdwgjxg==", "integrity": "sha512-vJnonTqmq3frCyTumJqG4g2IZcny3ynkfmbfDfQ90P3ZhRzcWYS/Um1ux6HFmAxmkaQnrZqIYHcGpL7kdqY8jA==",
"requires": { "requires": {
"bindings": "^1.3.0", "bindings": "^1.5.0",
"nan": "^2.10.0", "nan": "^2.13.2",
"prebuild-install": "^5.2.1" "prebuild-install": "^5.3.0"
} }
}, },
"noop-logger": { "noop-logger": {
@@ -289,13 +297,13 @@
}, },
"os-homedir": { "os-homedir": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
}, },
"prebuild-install": { "prebuild-install": {
"version": "5.2.2", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.2.2.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.0.tgz",
"integrity": "sha512-4e8VJnP3zJdZv/uP0eNWmr2r9urp4NECw7Mt1OSAi3rcLrbBRxGiAkfUFtre2MhQ5wfREAjRV+K1gubvs/GPsA==", "integrity": "sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg==",
"requires": { "requires": {
"detect-libc": "^1.0.3", "detect-libc": "^1.0.3",
"expand-template": "^2.0.3", "expand-template": "^2.0.3",
@@ -303,7 +311,7 @@
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"napi-build-utils": "^1.0.1", "napi-build-utils": "^1.0.1",
"node-abi": "^2.2.0", "node-abi": "^2.7.0",
"noop-logger": "^0.1.1", "noop-logger": "^0.1.1",
"npmlog": "^4.0.1", "npmlog": "^4.0.1",
"os-homedir": "^1.0.1", "os-homedir": "^1.0.1",
@@ -316,9 +324,9 @@
} }
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"pump": { "pump": {
"version": "2.0.1", "version": "2.0.1",
@@ -342,7 +350,7 @@
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": { "requires": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
@@ -360,9 +368,9 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}, },
"semver": { "semver": {
"version": "5.6.0", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
@@ -391,7 +399,7 @@
}, },
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
@@ -401,7 +409,7 @@
}, },
"string_decoder": { "string_decoder": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": { "requires": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
@@ -409,7 +417,7 @@
}, },
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
@@ -516,9 +524,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"xtend": { "xtend": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
} }
} }
} }

View File

@@ -9,15 +9,11 @@
"url": "git@github.com:UltimateHackingKeyboard/agent.git" "url": "git@github.com:UltimateHackingKeyboard/agent.git"
}, },
"license": "GPL-3.0", "license": "GPL-3.0",
"engines": {
"node": ">=8.12.0 <9.0.0",
"npm": ">=6.4.1 <7.0.0"
},
"dependencies": { "dependencies": {
"debug": "^4.1.1", "debug": "^4.1.1",
"byte-data": "^16.0.3", "byte-data": "^16.0.3",
"tslib": "^1.10.0", "tslib": "^1.10.0",
"node-hid": ">= 0.7.3" "node-hid": ">= 0.7.9"
}, },
"peer-dependencies": {}, "peer-dependencies": {},
"scripts": { "scripts": {

View File

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

View File

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

View File

@@ -32,9 +32,9 @@
} }
}, },
"base64-js": { "base64-js": {
"version": "0.0.8", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
}, },
"bindings": { "bindings": {
"version": "1.5.0", "version": "1.5.0",
@@ -54,24 +54,42 @@
} }
}, },
"buffer": { "buffer": {
"version": "3.6.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.0.tgz",
"integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", "integrity": "sha512-Xpgy0IwHK2N01ncykXTy6FpCWuM+CJSHoPVBLyNqyrWxsedpLvwsYUhf0ME3WRFNUhos0dMamz9cOS/xRDtU5g==",
"requires": { "requires": {
"base64-js": "0.0.8", "base64-js": "^1.0.2",
"ieee754": "^1.1.4", "ieee754": "^1.1.4"
"isarray": "^1.0.0"
} }
}, },
"buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
"requires": {
"buffer-alloc-unsafe": "^1.1.0",
"buffer-fill": "^1.0.0"
}
},
"buffer-alloc-unsafe": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
},
"buffer-crc32": { "buffer-crc32": {
"version": "0.2.13", "version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
}, },
"buffer-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
},
"chownr": { "chownr": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
@@ -231,9 +249,9 @@
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
}, },
"fd-slicer": { "fd-slicer": {
"version": "1.0.1", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
"requires": { "requires": {
"pend": "~1.2.0" "pend": "~1.2.0"
} }
@@ -267,6 +285,11 @@
} }
} }
}, },
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"gauge": { "gauge": {
"version": "2.7.4", "version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@@ -297,9 +320,9 @@
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
}, },
"graceful-fs": { "graceful-fs": {
"version": "4.1.11", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw=="
}, },
"graceful-readlink": { "graceful-readlink": {
"version": "1.0.1", "version": "1.0.1",
@@ -312,14 +335,14 @@
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
}, },
"ieee754": { "ieee754": {
"version": "1.1.11", "version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==" "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -350,9 +373,9 @@
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
}, },
"make-dir": { "make-dir": {
"version": "1.2.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
"integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
"requires": { "requires": {
"pify": "^3.0.0" "pify": "^3.0.0"
}, },
@@ -400,17 +423,17 @@
"integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==" "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA=="
}, },
"node-abi": { "node-abi": {
"version": "2.8.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.8.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.11.0.tgz",
"integrity": "sha512-1/aa2clS0pue0HjckL62CsbhWWU35HARvBDXcJtYKbYR7LnIutmpxmXbuDMV9kEviD2lP/wACOgWmmwljghHyQ==", "integrity": "sha512-kuy/aEg75u40v378WRllQ4ZexaXJiCvB68D2scDXclp/I4cRq6togpbOoKhmN07tns9Zldu51NNERo0wehfX9g==",
"requires": { "requires": {
"semver": "^5.4.1" "semver": "^5.4.1"
} }
}, },
"node-hid": { "node-hid": {
"version": "0.7.8", "version": "0.7.9",
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.8.tgz", "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.9.tgz",
"integrity": "sha512-79Z9hw/pqIDp0kxvb353ivGgslo4i0hYQTcCqfRFxIJSO2gF9VtPla5uQY/9jTcDlON5O5YaqxbdH+8bs+m+1Q==", "integrity": "sha512-vJnonTqmq3frCyTumJqG4g2IZcny3ynkfmbfDfQ90P3ZhRzcWYS/Um1ux6HFmAxmkaQnrZqIYHcGpL7kdqY8jA==",
"requires": { "requires": {
"bindings": "^1.5.0", "bindings": "^1.5.0",
"nan": "^2.13.2", "nan": "^2.13.2",
@@ -508,9 +531,9 @@
} }
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"pump": { "pump": {
"version": "2.0.1", "version": "2.0.1",
@@ -544,12 +567,19 @@
"safe-buffer": "~5.1.1", "safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1", "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1" "util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
} }
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.1", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}, },
"seek-bzip": { "seek-bzip": {
"version": "1.0.5", "version": "1.0.5",
@@ -560,9 +590,9 @@
} }
}, },
"semver": { "semver": {
"version": "5.7.0", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
@@ -605,6 +635,13 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": { "requires": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
} }
}, },
"strip-ansi": { "strip-ansi": {
@@ -656,13 +693,16 @@
} }
}, },
"tar-stream": { "tar-stream": {
"version": "1.5.5", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
"integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
"requires": { "requires": {
"bl": "^1.0.0", "bl": "^1.0.0",
"buffer-alloc": "^1.2.0",
"end-of-stream": "^1.0.0", "end-of-stream": "^1.0.0",
"readable-stream": "^2.0.0", "fs-constants": "^1.0.0",
"readable-stream": "^2.3.0",
"to-buffer": "^1.1.1",
"xtend": "^4.0.0" "xtend": "^4.0.0"
} }
}, },
@@ -698,6 +738,11 @@
"os-tmpdir": "~1.0.2" "os-tmpdir": "~1.0.2"
} }
}, },
"to-buffer": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
"integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
},
"tslib": { "tslib": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
@@ -717,12 +762,12 @@
"integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
}, },
"unbzip2-stream": { "unbzip2-stream": {
"version": "1.2.5", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz",
"integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==",
"requires": { "requires": {
"buffer": "^3.0.1", "buffer": "^5.2.1",
"through": "^2.3.6" "through": "^2.3.8"
} }
}, },
"util-deprecate": { "util-deprecate": {
@@ -749,17 +794,17 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"xtend": { "xtend": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
}, },
"yauzl": { "yauzl": {
"version": "2.9.1", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
"requires": { "requires": {
"buffer-crc32": "~0.2.3", "buffer-crc32": "~0.2.3",
"fd-slicer": "~1.0.1" "fd-slicer": "~1.1.0"
} }
} }
} }

View File

@@ -17,7 +17,7 @@
"command-line-args": "4.0.7", "command-line-args": "4.0.7",
"decompress": "4.2.0", "decompress": "4.2.0",
"decompress-bzip2": "4.0.0", "decompress-bzip2": "4.0.0",
"node-hid": "0.7.8", "node-hid": "0.7.9",
"sudo-prompt": "7.0.0", "sudo-prompt": "7.0.0",
"tmp": "0.0.33", "tmp": "0.0.33",
"tslib": "1.10.0", "tslib": "1.10.0",
@@ -29,6 +29,7 @@
"scripts": { "scripts": {
"start": "cross-env DEBUG=kboot* electron ./dist/electron-main.js", "start": "cross-env DEBUG=kboot* electron ./dist/electron-main.js",
"electron:spe": "electron ./dist/electron-main.js --spe", "electron:spe": "electron ./dist/electron-main.js --spe",
"electron:kboot": "cross-env DEBUG=kboot* electron ./dist/electron-main.js --useKboot",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-to-tmp-folder", "build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-to-tmp-folder",
"build:usb": "electron-rebuild -w node-hid -p -m ./dist", "build:usb": "electron-rebuild -w node-hid -p -m ./dist",
"lint": "tslint --project tsconfig.json", "lint": "tslint --project tsconfig.json",

View File

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

View File

@@ -1,4 +1,4 @@
import { app, BrowserWindow, Menu, systemPreferences } from 'electron'; import { app, BrowserWindow, Menu, MenuItemConstructorOptions, systemPreferences } from 'electron';
import * as isDev from 'electron-is-dev'; import * as isDev from 'electron-is-dev';
export const setMenu = (win: BrowserWindow): void => { export const setMenu = (win: BrowserWindow): void => {
@@ -8,7 +8,7 @@ export const setMenu = (win: BrowserWindow): void => {
return; return;
} }
const template = [ const template: MenuItemConstructorOptions[] = [
{ {
label: app.getName(), label: app.getName(),
submenu: [ submenu: [

View File

@@ -14,6 +14,6 @@
"npm": ">=5.1.0 <6.0.0" "npm": ">=5.1.0 <6.0.0"
}, },
"dependencies": { "dependencies": {
"node-hid": "0.7.8" "node-hid": "0.7.9"
} }
} }

View File

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

View File

@@ -15,7 +15,12 @@ export enum MouseActionParam {
scrollLeft, scrollLeft,
scrollRight, scrollRight,
accelerate, accelerate,
decelerate decelerate,
button4,
button5,
button6,
button7,
button8
} }
export class MouseAction extends KeyAction { export class MouseAction extends KeyAction {

View File

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

View File

@@ -1,6 +1,7 @@
import { HardwareModuleInfo } from './hardware-module-info'; import { LeftModuleInfo } from './left-module-info';
import { RightModuleInfo } from './right-module-info';
export interface HardwareModules { export interface HardwareModules {
leftModuleInfo?: HardwareModuleInfo; leftModuleInfo?: LeftModuleInfo;
rightModuleInfo?: HardwareModuleInfo; rightModuleInfo?: RightModuleInfo;
} }

View File

@@ -6,8 +6,9 @@ export * from './app-start-info';
export * from './configuration-reply'; export * from './configuration-reply';
export * from './version-information'; export * from './version-information';
export * from './device-connection-state'; export * from './device-connection-state';
export * from './left-module-info';
export * from './hardware-modules'; export * from './hardware-modules';
export * from './hardware-module-info'; export * from './right-module-info';
export * from './save-user-configuration-data'; export * from './save-user-configuration-data';
export * from './udev-rules-info'; export * from './udev-rules-info';
export * from './update-firmware-data'; export * from './update-firmware-data';

View File

@@ -1,4 +1,4 @@
export interface HardwareModuleInfo { export interface LeftModuleInfo {
firmwareVersion?: string; firmwareVersion?: string;
moduleProtocolVersion?: string; moduleProtocolVersion?: string;
} }

View File

@@ -0,0 +1,7 @@
export interface RightModuleInfo {
deviceProtocolVersion?: string;
hardwareConfigVersion?: string;
firmwareVersion?: string;
moduleProtocolVersion?: string;
userConfigVersion?: string;
}

View File

@@ -60,9 +60,9 @@
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
}, },
"chownr": { "chownr": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
@@ -151,9 +151,9 @@
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -209,17 +209,17 @@
"integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==" "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA=="
}, },
"node-abi": { "node-abi": {
"version": "2.8.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.8.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.11.0.tgz",
"integrity": "sha512-1/aa2clS0pue0HjckL62CsbhWWU35HARvBDXcJtYKbYR7LnIutmpxmXbuDMV9kEviD2lP/wACOgWmmwljghHyQ==", "integrity": "sha512-kuy/aEg75u40v378WRllQ4ZexaXJiCvB68D2scDXclp/I4cRq6togpbOoKhmN07tns9Zldu51NNERo0wehfX9g==",
"requires": { "requires": {
"semver": "^5.4.1" "semver": "^5.4.1"
} }
}, },
"node-hid": { "node-hid": {
"version": "0.7.8", "version": "0.7.9",
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.8.tgz", "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.9.tgz",
"integrity": "sha512-79Z9hw/pqIDp0kxvb353ivGgslo4i0hYQTcCqfRFxIJSO2gF9VtPla5uQY/9jTcDlON5O5YaqxbdH+8bs+m+1Q==", "integrity": "sha512-vJnonTqmq3frCyTumJqG4g2IZcny3ynkfmbfDfQ90P3ZhRzcWYS/Um1ux6HFmAxmkaQnrZqIYHcGpL7kdqY8jA==",
"requires": { "requires": {
"bindings": "^1.5.0", "bindings": "^1.5.0",
"nan": "^2.13.2", "nan": "^2.13.2",
@@ -294,9 +294,9 @@
} }
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"pump": { "pump": {
"version": "2.0.1", "version": "2.0.1",
@@ -338,9 +338,9 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}, },
"semver": { "semver": {
"version": "5.7.0", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
@@ -476,9 +476,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"xtend": { "xtend": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
} }
} }
} }

View File

@@ -12,7 +12,7 @@
}, },
"dependencies": { "dependencies": {
"kboot": "0.0.0", "kboot": "0.0.0",
"node-hid": "0.7.8", "node-hid": "0.7.9",
"nrf-intel-hex": "1.3.0", "nrf-intel-hex": "1.3.0",
"tslib": "1.10.0", "tslib": "1.10.0",
"uhk-common": "1.0.0" "uhk-common": "1.0.0"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -126,7 +126,9 @@ export const getFileContentAsync = async (filePath: string): Promise<Array<strin
export const readBootloaderFirmwareFromHexFileAsync = async (hexFilePath: string): Promise<Map<any, any>> => { export const readBootloaderFirmwareFromHexFileAsync = async (hexFilePath: string): Promise<Map<any, any>> => {
const fileContent = await readFile(hexFilePath, { encoding: 'utf8' }); const fileContent = await readFile(hexFilePath, { encoding: 'utf8' });
const memoryMap = MemoryMap.fromHex(fileContent); const fromHex = MemoryMap.fromHex ? MemoryMap.fromHex : MemoryMap.default.fromHex;
const memoryMap = fromHex(fileContent);
return memoryMap; return memoryMap;
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -19,29 +19,28 @@
}, },
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@angular/animations": "7.2.15", "@angular/animations": "8.2.6",
"@angular-builders/custom-webpack": "7.3.1", "@angular-builders/custom-webpack": "8.2.0",
"@angular/cli": "7.3.9", "@angular/cli": "8.3.4",
"@angular/common": "7.2.15", "@angular/common": "8.2.6",
"@angular/compiler": "7.2.15", "@angular/compiler": "8.2.6",
"@angular/compiler-cli": "7.2.15", "@angular/compiler-cli": "8.2.6",
"@angular/core": "7.2.15", "@angular/core": "8.2.6",
"@angular-devkit/build-angular": "0.13.9", "@angular-devkit/build-angular": "0.803.4",
"@angular-devkit/build-optimizer": "0.13.9", "@angular-devkit/build-optimizer": "0.803.4",
"@angular-devkit/core": "7.3.9", "@angular-devkit/core": "8.3.4",
"@angular/forms": "7.2.15", "@angular/forms": "8.2.6",
"@angular/http": "7.2.15", "@angular/language-service": "8.2.6",
"@angular/language-service": "7.2.15", "@angular/platform-browser": "8.2.6",
"@angular/platform-browser": "7.2.15", "@angular/platform-browser-dynamic": "8.2.6",
"@angular/platform-browser-dynamic": "7.2.15", "@angular/router": "8.2.6",
"@angular/router": "7.2.15", "@ngtools/webpack": "8.3.4",
"@ngtools/webpack": "7.3.9", "@ngrx/effects": "8.2.0",
"@ngrx/effects": "7.4.0", "@ngrx/router-store": "8.2.0",
"@ngrx/router-store": "7.4.0", "@ngrx/store": "8.2.0",
"@ngrx/store": "7.4.0", "@ngrx/store-devtools": "8.2.0",
"@ngrx/store-devtools": "7.4.0",
"@ngrx/store-log-monitor": "3.0.2", "@ngrx/store-log-monitor": "3.0.2",
"angular-confirmation-popover": "4.2.0", "angular-confirmation-popover": "4.2.1",
"angular-notifier": "4.1.1", "angular-notifier": "4.1.1",
"bootstrap": "3.4.1", "bootstrap": "3.4.1",
"codelyzer": "4.5.0", "codelyzer": "4.5.0",
@@ -56,19 +55,18 @@
"karma-jasmine": "1.1.2", "karma-jasmine": "1.1.2",
"karma-jasmine-html-reporter": "1.3.1", "karma-jasmine-html-reporter": "1.3.1",
"ng2-dragula": "2.1.1", "ng2-dragula": "2.1.1",
"ng2-nouislider": "1.7.13", "ng2-nouislider": "1.8.2",
"ngx-clipboard": "10.0.0", "ngx-clipboard": "12.2.0",
"ngx-select-ex": "3.6.8", "ngx-select-ex": "3.7.0",
"ngrx-store-freeze": "0.1.9", "ngrx-store-freeze": "0.2.4",
"nouislider": "13.1.1", "nouislider": "14.0.2",
"protractor": "5.4.0", "protractor": "5.4.0",
"reselect": "3.0.1", "rxjs": "6.5.2",
"rxjs": "6.4.0",
"semver": "5.6.0", "semver": "5.6.0",
"ts-keycode-enum": "^1.0.6", "ts-keycode-enum": "^1.0.6",
"uhk-common": "1.0.0", "uhk-common": "1.0.0",
"xml-loader": "1.2.1", "xml-loader": "1.2.1",
"zone.js": "0.8.26" "zone.js": "0.10.0"
}, },
"dependencies": { "dependencies": {
"classlist.js": "1.1.20150312", "classlist.js": "1.1.20150312",

View File

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

View File

@@ -1,7 +1,7 @@
import { Component, Input, ViewChild, ElementRef, OnInit } from '@angular/core'; import { Component, Input, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '../../../../store/index'; import { AppState } from '../../../../store';
import { UHKContributor } from '../../../../models/uhk-contributor'; import { UHKContributor } from '../../../../models/uhk-contributor';
@@ -10,9 +10,9 @@ import { UHKContributor } from '../../../../models/uhk-contributor';
templateUrl: './contributor-badge.component.html', templateUrl: './contributor-badge.component.html',
styleUrls: ['./contributor-badge.component.scss'] styleUrls: ['./contributor-badge.component.scss']
}) })
export class ContributorBadgeComponent implements OnInit { export class ContributorBadgeComponent implements AfterViewInit {
@Input() contributor: UHKContributor; @Input() contributor: UHKContributor;
@ViewChild('badge') badge: ElementRef; @ViewChild('badge', { static: false }) badge: ElementRef;
get name(): string { get name(): string {
return this.contributor.login; return this.contributor.login;
@@ -29,7 +29,7 @@ export class ContributorBadgeComponent implements OnInit {
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {
} }
ngOnInit(): void { ngAfterViewInit(): void {
(this.badge.nativeElement as HTMLImageElement).src = URL.createObjectURL(this.contributor.avatar); (this.badge.nativeElement as HTMLImageElement).src = URL.createObjectURL(this.contributor.avatar);
} }
} }

View File

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

View File

@@ -30,7 +30,7 @@ export class AutoGrowInputComponent implements ControlValueAccessor {
@Input() maxParentWidthPercent = 1; @Input() maxParentWidthPercent = 1;
@Input() css: string; @Input() css: string;
@ViewChild('inputControl') inputControl: ElementRef; @ViewChild('inputControl', { static: true }) inputControl: ElementRef;
disabled: boolean; disabled: boolean;

View File

@@ -21,7 +21,7 @@
<li> <li>
<button class="btn btn-danger" <button class="btn btn-danger"
mwlConfirmationPopover mwlConfirmationPopover
title="Are you sure?" popoverTitle="Are you sure?"
placement="bottom" placement="bottom"
confirmText="Yes" confirmText="Yes"
cancelText="No" cancelText="No"

View File

@@ -27,7 +27,7 @@
label="Choose firmware file and flash it"></file-upload> label="Choose firmware file and flash it"></file-upload>
</p> </p>
<div *ngIf="firmwareUpgradeFailed$ | async" <div *ngIf="firmwareUpgradeFailed"
class="alert alert-danger" class="alert alert-danger"
role="alert"> role="alert">
<p>Firmware update failed. Disconnect every USB device from your computer (including USB hubs, KVM switches, USB dongles, and everything else), then connect only your UHK and retry.</p> <p>Firmware update failed. Disconnect every USB device from your computer (including USB hubs, KVM switches, USB dongles, and everything else), then connect only your UHK and retry.</p>
@@ -35,7 +35,7 @@
<p>If you've tried the above and the update still keeps failing, please <a class="link-github" [href]="firmwareGithubIssueUrl" externalUrl>create a GitHub issue</a>, and attach the update log.</p> <p>If you've tried the above and the update still keeps failing, please <a class="link-github" [href]="firmwareGithubIssueUrl" externalUrl>create a GitHub issue</a>, and attach the update log.</p>
</div> </div>
<div *ngIf="firmwareUpgradeSuccess$ | async" <div *ngIf="firmwareUpgradeSuccess"
class="alert alert-success" class="alert alert-success"
role="alert"> role="alert">
<p>Firmware update succeeded.</p> <p>Firmware update succeeded.</p>

View File

@@ -1,4 +1,4 @@
import { Component, OnDestroy } from '@angular/core'; import { Component, OnDestroy, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { Constants, HardwareModules, VersionInformation } from 'uhk-common'; import { Constants, HardwareModules, VersionInformation } from 'uhk-common';
@@ -17,6 +17,7 @@ import {
import { UpdateFirmwareAction, UpdateFirmwareWithAction } from '../../../store/actions/device'; import { UpdateFirmwareAction, UpdateFirmwareWithAction } from '../../../store/actions/device';
import { XtermLog } from '../../../models/xterm-log'; import { XtermLog } from '../../../models/xterm-log';
import { UploadFileData } from '../../../models/upload-file-data'; import { UploadFileData } from '../../../models/upload-file-data';
import { XtermComponent } from '../../xterm/xterm.component';
@Component({ @Component({
selector: 'device-firmware', selector: 'device-firmware',
@@ -30,30 +31,41 @@ export class DeviceFirmwareComponent implements OnDestroy {
flashFirmwareButtonDisbabled$: Observable<boolean>; flashFirmwareButtonDisbabled$: Observable<boolean>;
xtermLog$: Observable<Array<XtermLog>>; xtermLog$: Observable<Array<XtermLog>>;
getAgentVersionInfo$: Observable<VersionInformation>; getAgentVersionInfo$: Observable<VersionInformation>;
hardwareModulesSubscription: Subscription;
hardwareModules: HardwareModules; hardwareModules: HardwareModules;
runningOnNotSupportedWindows$: Observable<boolean>; runningOnNotSupportedWindows$: Observable<boolean>;
firmwareUpgradeAllowed$: Observable<boolean>; firmwareUpgradeAllowed$: Observable<boolean>;
firmwareUpgradeFailed$: Observable<boolean>;
firmwareUpgradeSuccess$: Observable<boolean>;
firmwareGithubIssueUrl: string; firmwareGithubIssueUrl: string;
firmwareUpgradeFailed: boolean;
firmwareUpgradeSuccess: boolean;
@ViewChild(XtermComponent, { static: false })
xtermRef: XtermComponent;
private subscription = new Subscription();
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {
this.flashFirmwareButtonDisbabled$ = store.select(flashFirmwareButtonDisbabled); this.flashFirmwareButtonDisbabled$ = store.select(flashFirmwareButtonDisbabled);
this.xtermLog$ = store.select(xtermLog); this.xtermLog$ = store.select(xtermLog);
this.getAgentVersionInfo$ = store.select(getAgentVersionInfo); this.getAgentVersionInfo$ = store.select(getAgentVersionInfo);
this.hardwareModulesSubscription = store.select(getHardwareModules).subscribe(data => { this.subscription.add(store.select(getHardwareModules).subscribe(data => {
this.hardwareModules = data; this.hardwareModules = data;
}); }));
this.runningOnNotSupportedWindows$ = store.select(runningOnNotSupportedWindows); this.runningOnNotSupportedWindows$ = store.select(runningOnNotSupportedWindows);
this.firmwareUpgradeAllowed$ = store.select(firmwareUpgradeAllowed); this.firmwareUpgradeAllowed$ = store.select(firmwareUpgradeAllowed);
this.firmwareUpgradeFailed$ = store.select(firmwareUpgradeFailed); this.subscription.add(store.select(firmwareUpgradeFailed).subscribe(data => {
this.firmwareUpgradeSuccess$ = store.select(firmwareUpgradeSuccess); this.firmwareUpgradeFailed = data;
this.scrollToTheEndOfTheLogs();
}));
this.subscription.add(store.select(firmwareUpgradeSuccess).subscribe(data => {
this.firmwareUpgradeSuccess = data;
this.scrollToTheEndOfTheLogs();
}));
this.firmwareGithubIssueUrl = Constants.FIRMWARE_GITHUB_ISSUE_URL; this.firmwareGithubIssueUrl = Constants.FIRMWARE_GITHUB_ISSUE_URL;
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.hardwareModulesSubscription.unsubscribe(); this.subscription.unsubscribe();
} }
onUpdateFirmware(): void { onUpdateFirmware(): void {
@@ -63,4 +75,10 @@ export class DeviceFirmwareComponent implements OnDestroy {
changeFile(data: UploadFileData): void { changeFile(data: UploadFileData): void {
this.store.dispatch(new UpdateFirmwareWithAction(data.data)); this.store.dispatch(new UpdateFirmwareWithAction(data.data));
} }
private scrollToTheEndOfTheLogs(): void {
if (this.xtermRef) {
this.xtermRef.scrollToTheEnd();
}
}
} }

View File

@@ -4,7 +4,7 @@
</h1> </h1>
<button class="btn btn-danger mouse-speed-reset-button" <button class="btn btn-danger mouse-speed-reset-button"
mwlConfirmationPopover mwlConfirmationPopover
title="Are you sure?" popoverTitle="Are you sure?"
placement="bottom" placement="bottom"
confirmText="Yes" confirmText="Yes"
cancelText="No" cancelText="No"
@@ -12,7 +12,7 @@
</button> </button>
<button class="btn btn-danger mouse-speed-reset-button" <button class="btn btn-danger mouse-speed-reset-button"
mwlConfirmationPopover mwlConfirmationPopover
title="Are you sure?" popoverTitle="Are you sure?"
placement="bottom" placement="bottom"
confirmText="Yes" confirmText="Yes"
cancelText="No" cancelText="No"

View File

@@ -39,8 +39,8 @@ export class KeymapHeaderComponent implements OnChanges {
@Input() deletable: boolean; @Input() deletable: boolean;
@Output() downloadClick = new EventEmitter<void>(); @Output() downloadClick = new EventEmitter<void>();
@ViewChild('name') keymapName: ElementRef; @ViewChild('name', { static: true }) keymapName: ElementRef;
@ViewChild('abbr') keymapAbbr: ElementRef; @ViewChild('abbr', { static: true }) keymapAbbr: ElementRef;
starTitle: string; starTitle: string;
trashTitle: string = DEFAULT_TRASH_TITLE; trashTitle: string = DEFAULT_TRASH_TITLE;

View File

@@ -31,7 +31,8 @@ export class MacroActionEditorComponent implements OnInit {
@Output() save = new EventEmitter<MacroAction>(); @Output() save = new EventEmitter<MacroAction>();
@Output() cancel = new EventEmitter<void>(); @Output() cancel = new EventEmitter<void>();
@ViewChild('tab') selectedTab: MacroTextTabComponent | MacroKeyTabComponent | MacroMouseTabComponent | MacroDelayTabComponent; // tslint:disable-next-line:max-line-length
@ViewChild('tab', { static: false }) selectedTab: MacroTextTabComponent | MacroKeyTabComponent | MacroMouseTabComponent | MacroDelayTabComponent;
editableMacroAction: MacroAction; editableMacroAction: MacroAction;
activeTab: TabName; activeTab: TabName;

View File

@@ -1,10 +1,8 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ElementRef,
Input, Input,
OnInit, OnInit
ViewChild
} from '@angular/core'; } from '@angular/core';
import { DelayMacroAction } from 'uhk-common'; import { DelayMacroAction } from 'uhk-common';
@@ -21,7 +19,6 @@ const INITIAL_DELAY = 0.5; // In seconds
}) })
export class MacroDelayTabComponent extends MacroBaseComponent implements OnInit { export class MacroDelayTabComponent extends MacroBaseComponent implements OnInit {
@Input() macroAction: DelayMacroAction; @Input() macroAction: DelayMacroAction;
@ViewChild('macroDelayInput') input: ElementRef;
presets: number[] = [0.1, 0.5, 1, 5, 10]; presets: number[] = [0.1, 0.5, 1, 5, 10];

View File

@@ -21,8 +21,7 @@ enum TabName {
}) })
export class MacroKeyTabComponent extends MacroBaseComponent implements OnInit { export class MacroKeyTabComponent extends MacroBaseComponent implements OnInit {
@Input() macroAction: KeyMacroAction; @Input() macroAction: KeyMacroAction;
@ViewChild('tab') selectedTab: Tab; @ViewChild('keypressTab', { static: true }) keypressTab: KeypressTabComponent;
@ViewChild('keypressTab') keypressTab: KeypressTabComponent;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */ /* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
TabName = TabName; TabName = TabName;

View File

@@ -1,4 +1,4 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { import {
MacroMouseSubAction, MacroMouseSubAction,
@@ -7,7 +7,6 @@ import {
MoveMouseMacroAction, MoveMouseMacroAction,
ScrollMouseMacroAction ScrollMouseMacroAction
} from 'uhk-common'; } from 'uhk-common';
import { Tab } from '../../../../popover/tab';
import { MacroBaseComponent } from '../macro-base.component'; import { MacroBaseComponent } from '../macro-base.component';
type MouseMacroAction = MouseButtonMacroAction | MoveMouseMacroAction | ScrollMouseMacroAction; type MouseMacroAction = MouseButtonMacroAction | MoveMouseMacroAction | ScrollMouseMacroAction;
@@ -31,7 +30,6 @@ enum TabName {
}) })
export class MacroMouseTabComponent extends MacroBaseComponent implements OnInit { export class MacroMouseTabComponent extends MacroBaseComponent implements OnInit {
@Input() macroAction: MouseMacroAction; @Input() macroAction: MouseMacroAction;
@ViewChild('tab') selectedTab: Tab;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */ /* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
MouseButtons = MouseButtons; MouseButtons = MouseButtons;

View File

@@ -20,7 +20,7 @@ const NON_ASCII_REGEXP = /[^\x00-\x7F]/g;
}) })
export class MacroTextTabComponent extends MacroBaseComponent implements OnInit, AfterViewInit { export class MacroTextTabComponent extends MacroBaseComponent implements OnInit, AfterViewInit {
@Input() macroAction: TextMacroAction; @Input() macroAction: TextMacroAction;
@ViewChild('macroTextInput') input: ElementRef; @ViewChild('macroTextInput', { static: false } ) input: ElementRef;
constructor() { super(); } constructor() { super(); }

View File

@@ -26,7 +26,7 @@ import * as util from '../../../util';
export class MacroHeaderComponent implements AfterViewInit, OnChanges { export class MacroHeaderComponent implements AfterViewInit, OnChanges {
@Input() macro: Macro; @Input() macro: Macro;
@Input() isNew: boolean; @Input() isNew: boolean;
@ViewChild('macroName') macroName: ElementRef; @ViewChild('macroName', { static: true }) macroName: ElementRef;
constructor(private store: Store<AppState>, private renderer: Renderer2) { } constructor(private store: Store<AppState>, private renderer: Renderer2) { }

View File

@@ -46,7 +46,6 @@ export class MacroListComponent {
newMacro: Macro = undefined; newMacro: Macro = undefined;
showNew: boolean = false; showNew: boolean = false;
private activeEdit: number = undefined; private activeEdit: number = undefined;
private dragIndex: number;
constructor( constructor(
private mapper: MapperService, private mapper: MapperService,
@@ -59,16 +58,18 @@ export class MacroListComponent {
} }
}); });
dragulaService.drag('macroActions').subscribe((value: any) => { dragulaService.drop('macroActions').subscribe(value => {
this.dragIndex = +value[1].getAttribute('data-index'); if (value.el) {
}); let newIndex = this.macroItems.length - 1;
if (value.sibling) {
newIndex = (+value.sibling.getAttribute('data-index') - 1);
}
dragulaService.drop('macroActions').subscribe((value: any) => {
if (value[4]) {
this.reorder.emit({ this.reorder.emit({
macroId: this.macro.id, macroId: this.macro.id,
oldIndex: this.dragIndex, oldIndex: +value.el.getAttribute('data-index'),
newIndex: +value[4].getAttribute('data-index') newIndex
}); });
} }
}); });

View File

@@ -38,6 +38,7 @@
></layer-tab> ></layer-tab>
<mouse-tab #tab *ngSwitchCase="tabName.Mouse" class="popover-content" <mouse-tab #tab *ngSwitchCase="tabName.Mouse" class="popover-content"
[defaultKeyAction]="defaultKeyAction" [defaultKeyAction]="defaultKeyAction"
[extraMouseButtonsSupported]="extraMouseButtonsSupported$ | async"
(validAction)="setKeyActionValidState($event)" (validAction)="setKeyActionValidState($event)"
></mouse-tab> ></mouse-tab>
<macro-tab #tab *ngSwitchCase="tabName.Macro" class="popover-content" <macro-tab #tab *ngSwitchCase="tabName.Macro" class="popover-content"

View File

@@ -30,7 +30,7 @@ import {
import { Tab } from './tab'; import { Tab } from './tab';
import { AppState, getKeymaps, macroPlaybackSupported } from '../../store'; import { AppState, extraMouseButtonsSupported, getKeymaps, macroPlaybackSupported } from '../../store';
import { KeyActionRemap } from '../../models/key-action-remap'; import { KeyActionRemap } from '../../models/key-action-remap';
import { RemapInfo } from '../../models/remap-info'; import { RemapInfo } from '../../models/remap-info';
@@ -98,8 +98,8 @@ export class PopoverComponent implements OnChanges {
@Output() cancel = new EventEmitter<any>(); @Output() cancel = new EventEmitter<any>();
@Output() remap = new EventEmitter<KeyActionRemap>(); @Output() remap = new EventEmitter<KeyActionRemap>();
@ViewChild('tab') selectedTab: Tab; @ViewChild('tab', { static: false }) selectedTab: Tab;
@ViewChild('popover') popoverHost: ElementRef; @ViewChild('popover', { static: false }) popoverHost: ElementRef;
tabName = TabName; tabName = TabName;
keyActionValid: boolean; keyActionValid: boolean;
@@ -145,6 +145,7 @@ export class PopoverComponent implements OnChanges {
} }
]; ];
macroPlaybackSupported$: Observable<boolean>; macroPlaybackSupported$: Observable<boolean>;
extraMouseButtonsSupported$: Observable<boolean>;
private readonly currentKeymap$ = new BehaviorSubject<Keymap>(undefined); private readonly currentKeymap$ = new BehaviorSubject<Keymap>(undefined);
@@ -158,6 +159,7 @@ export class PopoverComponent implements OnChanges {
keymaps.filter((keymap: Keymap) => currentKeymap.abbreviation !== keymap.abbreviation)) keymaps.filter((keymap: Keymap) => currentKeymap.abbreviation !== keymap.abbreviation))
); );
this.macroPlaybackSupported$ = store.select(macroPlaybackSupported); this.macroPlaybackSupported$ = store.select(macroPlaybackSupported);
this.extraMouseButtonsSupported$ = store.select(extraMouseButtonsSupported);
} }
ngOnChanges(change: SimpleChanges) { ngOnChanges(change: SimpleChanges) {

View File

@@ -80,6 +80,27 @@
[class.btn-primary]="mouseActionParam === MouseActionParam.rightClick" [class.btn-primary]="mouseActionParam === MouseActionParam.rightClick"
(click)="setMouseActionParam(MouseActionParam.rightClick)">Right</button> (click)="setMouseActionParam(MouseActionParam.rightClick)">Right</button>
</div> </div>
<div class="additional-keys" *ngIf="extraMouseButtonsSupported">
<div class="btn-group col-xs-12" role="group">
<button type="button" class="btn btn-default col-xs-4"
[class.btn-primary]="mouseActionParam === MouseActionParam.button4"
(click)="setMouseActionParam(MouseActionParam.button4)">Button 4</button>
<button type="button" class="btn btn-default col-xs-4"
[class.btn-primary]="mouseActionParam === MouseActionParam.button5"
(click)="setMouseActionParam(MouseActionParam.button5)">Button 5</button>
<button type="button" class="btn btn-default col-xs-4"
[class.btn-primary]="mouseActionParam === MouseActionParam.button6"
(click)="setMouseActionParam(MouseActionParam.button6)">Button 6</button>
</div>
<div class="btn-group col-xs-12" role="group">
<button type="button" class="btn btn-default col-xs-6"
[class.btn-primary]="mouseActionParam === MouseActionParam.button7"
(click)="setMouseActionParam(MouseActionParam.button7)">Button 7</button>
<button type="button" class="btn btn-default col-xs-6"
[class.btn-primary]="mouseActionParam === MouseActionParam.button8"
(click)="setMouseActionParam(MouseActionParam.button8)">Button 8</button>
</div>
</div>
</div> </div>
<div *ngSwitchCase="3" class="mouse__config mouse__config--speed text-center"> <div *ngSwitchCase="3" class="mouse__config mouse__config--speed text-center">
<div class="help-text--mouse-speed text-left"> <div class="help-text--mouse-speed text-left">

View File

@@ -54,6 +54,32 @@
} }
} }
.mouse__config--click {
.additional-keys {
display: inline-block;
margin-top: 1rem;
.btn-group:first-child {
.btn {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.btn-group:last-child {
.btn {
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
&:last-child {
right: 1px;
}
}
}
}
}
.mouse__config--speed { .mouse__config--speed {
.btn-default { .btn-default {
font-size: 25px; font-size: 25px;

View File

@@ -11,6 +11,7 @@ import { Tab } from '../tab';
}) })
export class MouseTabComponent extends Tab implements OnChanges { export class MouseTabComponent extends Tab implements OnChanges {
@Input() defaultKeyAction: KeyAction; @Input() defaultKeyAction: KeyAction;
@Input() extraMouseButtonsSupported: boolean;
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */ /* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
MouseActionParam = MouseActionParam; MouseActionParam = MouseActionParam;
@@ -62,6 +63,11 @@ export class MouseTabComponent extends Tab implements OnChanges {
case MouseActionParam.leftClick: case MouseActionParam.leftClick:
case MouseActionParam.middleClick: case MouseActionParam.middleClick:
case MouseActionParam.rightClick: case MouseActionParam.rightClick:
case MouseActionParam.button4:
case MouseActionParam.button5:
case MouseActionParam.button6:
case MouseActionParam.button7:
case MouseActionParam.button8:
this.selectedPageIndex = 2; this.selectedPageIndex = 2;
break; break;
case MouseActionParam.decelerate: case MouseActionParam.decelerate:

View File

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

View File

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

View File

@@ -2,10 +2,9 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef, OnDestroy,
OnDestroy, OnInit, OnInit,
Renderer2, Renderer2
ViewChild
} from '@angular/core'; } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations'; import { animate, state, style, transition, trigger } from '@angular/animations';
@@ -38,7 +37,6 @@ import { SideMenuPageState } from '../../models/side-menu-page-state';
export class SideMenuComponent implements OnInit, OnDestroy { export class SideMenuComponent implements OnInit, OnDestroy {
state: SideMenuPageState; state: SideMenuPageState;
animation: { [key: string]: 'active' | 'inactive' }; animation: { [key: string]: 'active' | 'inactive' };
@ViewChild('deviceName') deviceName: ElementRef;
private stateSubscription: Subscription; private stateSubscription: Subscription;

View File

@@ -28,7 +28,7 @@ export interface SliderProps {
] ]
}) })
export class SliderWrapperComponent implements AfterViewInit, ControlValueAccessor, OnDestroy { export class SliderWrapperComponent implements AfterViewInit, ControlValueAccessor, OnDestroy {
@ViewChild(NouisliderComponent) slider: NouisliderComponent; @ViewChild(NouisliderComponent, { static: false }) slider: NouisliderComponent;
@Input() label: string; @Input() label: string;
@Input() tooltip: string; @Input() tooltip: string;
@Input() min: number; @Input() min: number;

View File

@@ -91,6 +91,7 @@ export class SvgKeyboardComponent {
this.modules = []; this.modules = [];
this.viewBox = '-520 582 1100 470'; this.viewBox = '-520 582 1100 470';
this.moduleAnimationStates = []; this.moduleAnimationStates = [];
this.moduleVisibilityAnimationStates = [];
} }
ngOnInit() { ngOnInit() {

View File

@@ -1,8 +1,4 @@
:host { :host {
/deep/ text {
dominant-baseline: central;
}
cursor: pointer; cursor: pointer;
outline: none; outline: none;

View File

@@ -79,7 +79,7 @@ export class SvgKeyboardKeyComponent implements OnChanges {
@Output() keyClick = new EventEmitter<SvgKeyClickEvent>(); @Output() keyClick = new EventEmitter<SvgKeyClickEvent>();
@Output() capture = new EventEmitter<SvgKeyCaptureEvent>(); @Output() capture = new EventEmitter<SvgKeyCaptureEvent>();
@ViewChild('svgRec') svgRec: ElementRef<HTMLElement>; @ViewChild('svgRec', { static: false }) svgRec: ElementRef<HTMLElement>;
enumLabelTypes = LabelTypes; enumLabelTypes = LabelTypes;

View File

@@ -25,17 +25,17 @@
</svg> </svg>
<svg viewBox="0 0 100 100" [attr.width]="control.width" [attr.height]="control.height" [attr.x]="control.x" [attr.y]="control.y" <svg viewBox="0 0 100 100" [attr.width]="control.width" [attr.height]="control.height" [attr.x]="control.x" [attr.y]="control.y"
preserveAspectRatio="none" [class.disabled]="control.disabled"> preserveAspectRatio="none" [class.disabled]="control.disabled">
<svg:text [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">C</svg:text> <svg:text text-anchor="middle" alignment-baseline="middle" x="50" y="55">C</svg:text>
</svg> </svg>
<svg viewBox="0 0 100 100" [attr.width]="option.width" [attr.height]="option.height" [attr.x]="option.x" [attr.y]="option.y" <svg viewBox="0 0 100 100" [attr.width]="option.width" [attr.height]="option.height" [attr.x]="option.x" [attr.y]="option.y"
preserveAspectRatio="none" [class.disabled]="option.disabled"> preserveAspectRatio="none" [class.disabled]="option.disabled">
<svg:use *ngIf="modifierIconNames.option" [attr.xlink:href]="modifierIconNames.option" /> <svg:use *ngIf="modifierIconNames.option" [attr.xlink:href]="modifierIconNames.option" />
<svg:text *ngIf="!modifierIconNames.option" [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">A</svg:text> <svg:text *ngIf="!modifierIconNames.option" text-anchor="middle" alignment-baseline="middle" x="50" y="55">A</svg:text>
</svg> </svg>
<svg viewBox="0 0 100 100" [attr.width]="command.width" [attr.height]="command.height" [attr.x]="command.x" [attr.y]="command.y" <svg viewBox="0 0 100 100" [attr.width]="command.width" [attr.height]="command.height" [attr.x]="command.x" [attr.y]="command.y"
preserveAspectRatio="none" [class.disabled]="command.disabled"> preserveAspectRatio="none" [class.disabled]="command.disabled">
<svg:use *ngIf="modifierIconNames.command" [attr.xlink:href]="modifierIconNames.command" /> <svg:use *ngIf="modifierIconNames.command" [attr.xlink:href]="modifierIconNames.command" />
<svg:text *ngIf="!modifierIconNames.command" [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">S</svg:text> <svg:text *ngIf="!modifierIconNames.command" text-anchor="middle" alignment-baseline="middle" x="50" y="55">S</svg:text>
</svg> </svg>
</svg> </svg>
<svg:g svg-secondary-role <svg:g svg-secondary-role

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -5,12 +5,12 @@
[attr.y]="0" [attr.y]="0"
[attr.text-anchor]="'middle'" [attr.text-anchor]="'middle'"
[attr.font-size]="25"> [attr.font-size]="25">
<tspan dy="34"> Click </tspan> <tspan dy="45"> Click </tspan>
</svg:text> </svg:text>
<svg:text <svg:text
[attr.x]="50" [attr.x]="50"
[attr.y]="0" [attr.y]="0"
[attr.text-anchor]="'middle'" [attr.text-anchor]="'middle'"
[attr.font-size]="25"> [attr.font-size]="25">
<tspan dy="70"> {{ button }} </tspan> <tspan dy="80"> {{ button }} </tspan>
</svg:text> </svg:text>

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 404 B

View File

@@ -28,6 +28,26 @@ export class SvgMouseKeyComponent implements OnChanges {
this.type = 'click'; this.type = 'click';
this.param = 'Middle'; this.param = 'Middle';
break; break;
case MouseActionParam.button4:
this.type = 'click';
this.param = 'Button 4';
break;
case MouseActionParam.button5:
this.type = 'click';
this.param = 'Button 5';
break;
case MouseActionParam.button6:
this.type = 'click';
this.param = 'Button 6';
break;
case MouseActionParam.button7:
this.type = 'click';
this.param = 'Button 7';
break;
case MouseActionParam.button8:
this.type = 'click';
this.param = 'Button 8';
break;
case MouseActionParam.scrollDown: case MouseActionParam.scrollDown:
this.type = 'scroll'; this.type = 'scroll';
this.param = 'down'; this.param = 'down';

View File

@@ -4,6 +4,6 @@
[attr.y]="0" [attr.y]="0"
[attr.text-anchor]="'middle'" [attr.text-anchor]="'middle'"
[attr.font-size]="24"> [attr.font-size]="24">
<tspan dy="34"> Move </tspan> <tspan dy="45"> Move </tspan>
</svg:text> </svg:text>
<svg:use [attr.xlink:href]="directionIcon" width="30" height="30" x="35" y="55"></svg:use> <svg:use [attr.xlink:href]="directionIcon" width="30" height="30" x="35" y="55"></svg:use>

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 333 B

View File

@@ -4,6 +4,6 @@
[attr.y]="0" [attr.y]="0"
[attr.text-anchor]="'middle'" [attr.text-anchor]="'middle'"
[attr.font-size]="24"> [attr.font-size]="24">
<tspan dy="34"> Scroll </tspan> <tspan dy="45"> Scroll </tspan>
</svg:text> </svg:text>
<svg:use [attr.xlink:href]="directionIcon" width="30" height="30" x="35" y="55"></svg:use> <svg:use [attr.xlink:href]="directionIcon" width="30" height="30" x="35" y="55"></svg:use>

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -5,12 +5,12 @@
[attr.y]="0" [attr.y]="0"
[attr.text-anchor]="'middle'" [attr.text-anchor]="'middle'"
[attr.font-size]="25"> [attr.font-size]="25">
<tspan dy="34"> Speed </tspan> <tspan dy="45"> Speed </tspan>
</svg:text> </svg:text>
<svg:text <svg:text
[attr.x]="50" [attr.x]="50"
[attr.y]="0" [attr.y]="0"
[attr.text-anchor]="'middle'" [attr.text-anchor]="'middle'"
[attr.font-size]="30"> [attr.font-size]="30">
<tspan dy="70"> {{ sign }} </tspan> <tspan dy="80"> {{ sign }} </tspan>
</svg:text> </svg:text>

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 401 B

View File

@@ -1,8 +1,9 @@
<svg:text <svg:text
[attr.x]="0" [attr.x]="textX"
[attr.y]="textY" [attr.y]="textY"
[attr.text-anchor]="'middle'"> text-anchor="middle"
<tspan [attr.x]="spanX" dy="0">{{ text }}</tspan> alignment-baseline="middle">
{{ text }}
</svg:text> </svg:text>
<svg:g svg-secondary-role <svg:g svg-secondary-role
*ngIf="secondaryText" *ngIf="secondaryText"

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 304 B

View File

@@ -14,8 +14,8 @@ export class SvgOneLineTextKeyComponent implements OnChanges {
@Input() text: string; @Input() text: string;
@Input() secondaryText: string; @Input() secondaryText: string;
textX: number;
textY: number; textY: number;
spanX: number;
secondaryTextY: number; secondaryTextY: number;
secondaryHeight: number; secondaryHeight: number;
@@ -34,8 +34,8 @@ export class SvgOneLineTextKeyComponent implements OnChanges {
secondaryYModifier = 5; secondaryYModifier = 5;
} }
this.textX = this.width / 2;
this.textY = this.height / 2 - textYModifier; this.textY = this.height / 2 - textYModifier;
this.spanX = this.width / 2;
this.secondaryHeight = this.height / 4; this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier; this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;

View File

@@ -1,14 +1,15 @@
<svg [attr.viewBox]="viewBox" [attr.width]="width" [attr.height]="height" [attr.y]="y"> <svg [attr.viewBox]="viewBox" [attr.width]="width" [attr.height]="height" [attr.y]="y">
<g id="secondaryContent" [attr.transform]="transform"> <g [attr.transform]="transform">
<svg viewBox="0 0 14 14" width="12" height="12" x="2" [attr.y]="textY / 3.5"> <svg viewBox="0 0 14 14" width="12" height="12" x="2" [attr.y]="textY / 3.5">
<ellipse stroke="#fff" rx="6.5" ry="6.5" cy="7" cx="7" stroke-width="1" fill-opacity="0"/> <ellipse stroke="#fff" rx="6.5" ry="6.5" cy="7" cx="7" stroke-width="1" fill-opacity="0"/>
<text text-anchor="start" font-family="Helvetica" font-size="12" y="7.8" x="4" stroke-width="0">2 <text text-anchor="start" alignment-baseline="middle" font-family="Helvetica" font-size="12" y="7.8" x="4" stroke-width="0">2
</text> </text>
</svg> </svg>
<text [attr.y]="textY" <text [attr.y]="textY"
[attr.x]="textIndent" [attr.x]="textIndent"
font-size="12" font-size="12"
text-anchor="start"> text-anchor="start"
alignment-baseline="middle">
{{ text }} {{ text }}
</text> </text>
</g> </g>

Before

Width:  |  Height:  |  Size: 667 B

After

Width:  |  Height:  |  Size: 715 B

View File

@@ -0,0 +1,3 @@
text {
dominant-baseline: text-bottom;
}

View File

@@ -1,12 +1,10 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ElementRef,
Input, Input,
OnChanges, OnChanges,
OnInit, OnInit,
SimpleChanges, SimpleChanges
ViewChild
} from '@angular/core'; } from '@angular/core';
import { getContentWidth } from '../../../../util'; import { getContentWidth } from '../../../../util';
@@ -18,6 +16,7 @@ const SECONDARY_STYLE: CSSStyleDeclaration = {
@Component({ @Component({
selector: 'g[svg-secondary-role]', selector: 'g[svg-secondary-role]',
templateUrl: './svg-secondary-role.component.html', templateUrl: './svg-secondary-role.component.html',
styleUrls: ['./svg-secondary-role.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgSecondaryRoleComponent implements OnInit, OnChanges { export class SvgSecondaryRoleComponent implements OnInit, OnChanges {
@@ -26,8 +25,6 @@ export class SvgSecondaryRoleComponent implements OnInit, OnChanges {
@Input() y: number; @Input() y: number;
@Input() text: string; @Input() text: string;
@ViewChild('secondary') svgElement: ElementRef;
viewBox: string; viewBox: string;
textY: number; textY: number;
transform: string; transform: string;

View File

@@ -5,8 +5,9 @@
[attr.y]="useY"> [attr.y]="useY">
</svg:use> </svg:use>
<svg:text <svg:text
[attr.x]="0" [attr.x]="textX"
[attr.y]="textY" [attr.y]="textY"
[attr.text-anchor]="'middle'"> text-anchor="middle"
<tspan [attr.x]="spanX">{{ abbreviation }}</tspan> alignment-baseline="middle">
{{ abbreviation }}
</svg:text> </svg:text>

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 305 B

View File

@@ -18,7 +18,7 @@ export class SvgSwitchKeymapKeyComponent implements OnInit {
useX: number; useX: number;
useY: number; useY: number;
textY: number; textY: number;
spanX: number; textX: number;
constructor(private mapperService: MapperService) { } constructor(private mapperService: MapperService) { }
@@ -30,6 +30,6 @@ export class SvgSwitchKeymapKeyComponent implements OnInit {
this.useX = this.width * 3 / 8; this.useX = this.width * 3 / 8;
this.useY = this.height / 5; this.useY = this.height / 5;
this.textY = this.height * 2 / 3; this.textY = this.height * 2 / 3;
this.spanX = this.width / 2; this.textX = this.width / 2;
} }
} }

View File

@@ -1,8 +1,9 @@
<svg:text <svg:text
[attr.x]="0" [attr.x]="textX"
[attr.y]="textY" [attr.y]="textY"
[attr.text-anchor]="textAnchor"> [attr.text-anchor]="textAnchor"
<tspan [attr.x]="spanX">{{ text }}</tspan> alignment-baseline="middle">
{{ text }}
</svg:text> </svg:text>
<svg:use [attr.xlink:href]="icon" <svg:use [attr.xlink:href]="icon"
[attr.width]="useWidth" [attr.width]="useWidth"

Before

Width:  |  Height:  |  Size: 484 B

After

Width:  |  Height:  |  Size: 482 B

View File

@@ -21,7 +21,7 @@ export class SvgTextIconKeyComponent implements OnChanges {
useY: number; useY: number;
textY: number; textY: number;
textAnchor: string; textAnchor: string;
spanX: number; textX: number;
secondaryTextY: number; secondaryTextY: number;
secondaryHeight: number; secondaryHeight: number;
@@ -46,7 +46,7 @@ export class SvgTextIconKeyComponent implements OnChanges {
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 2; this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 2;
this.textY = ((this.width > 2 * this.height) ? this.height / 2 : this.height / 3) - textYModifier; this.textY = ((this.width > 2 * this.height) ? this.height / 2 : this.height / 3) - textYModifier;
this.textAnchor = (this.width > 2 * this.height) ? 'end' : 'middle'; this.textAnchor = (this.width > 2 * this.height) ? 'end' : 'middle';
this.spanX = (this.width > 2 * this.height) ? 0.6 * this.width : this.width / 2; this.textX = (this.width > 2 * this.height) ? 0.6 * this.width : this.width / 2;
this.secondaryHeight = this.height / 4; this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier; this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;

View File

@@ -1,12 +1,12 @@
<svg:text <svg:text
[attr.x]="0" [attr.x]="0"
[attr.y]="textY" [attr.y]="textY"
[attr.text-anchor]="'middle'"> text-anchor="middle"
alignment-baseline="middle">
<tspan <tspan
*ngFor="let text of texts; let index = index" *ngFor="let text of texts; let index = index"
[attr.x]="spanX" [attr.x]="spanX"
[attr.y]="spanYs[index]" [attr.y]="spanYs[index]"
dy="0"
>{{ text }}</tspan> >{{ text }}</tspan>
</svg:text> </svg:text>
<svg:g svg-secondary-role <svg:g svg-secondary-role

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -27,13 +27,11 @@ export class SvgTwoLineTextKeyComponent implements OnChanges {
} }
calculatePositions(): void { calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0; let secondaryYModifier = 0;
this.secondaryHeight = 0; this.secondaryHeight = 0;
let textContainerHeight = this.height; let textContainerHeight = this.height;
if (this.secondaryText) { if (this.secondaryText) {
textYModifier = this.height / 5;
secondaryYModifier = 0; secondaryYModifier = 0;
this.secondaryHeight = this.height / 4; this.secondaryHeight = this.height / 4;
textContainerHeight -= this.secondaryHeight; textContainerHeight -= this.secondaryHeight;
@@ -43,7 +41,7 @@ export class SvgTwoLineTextKeyComponent implements OnChanges {
this.spanX = this.width / 2; this.spanX = this.width / 2;
this.spanYs = []; this.spanYs = [];
for (let i = 0; i < this.texts.length; ++i) { for (let i = 0; i < this.texts.length; ++i) {
this.spanYs.push((0.75 - i * 0.5) * textContainerHeight); this.spanYs.push((0.85 - i * 0.5) * textContainerHeight);
} }
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier; this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;

View File

@@ -73,7 +73,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
@Output() descriptionChanged = new EventEmitter<ChangeKeymapDescription>(); @Output() descriptionChanged = new EventEmitter<ChangeKeymapDescription>();
@ViewChild(PopoverComponent, { read: ElementRef }) popover: ElementRef; @ViewChild(PopoverComponent, { read: ElementRef, static: false }) popover: ElementRef;
popoverShown: boolean; popoverShown: boolean;
keyEditConfig: { moduleId: number, keyId: number }; keyEditConfig: { moduleId: number, keyId: number };
@@ -218,7 +218,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
return; return;
} }
const el: Element = event.target as Element || event.srcElement; const el = event.target as Element;
const position: ClientRect = el.getBoundingClientRect(); const position: ClientRect = el.getBoundingClientRect();
let posLeft: number = this.tooltipData.posLeft; let posLeft: number = this.tooltipData.posLeft;
let posTop: number = this.tooltipData.posTop; let posTop: number = this.tooltipData.posTop;

View File

@@ -10,13 +10,11 @@ import { XtermLog } from '../../models/xterm-log';
export class XtermComponent implements OnChanges { export class XtermComponent implements OnChanges {
@Input() logs: Array<XtermLog> = []; @Input() logs: Array<XtermLog> = [];
@ViewChild('scrollMe') divElement: ElementRef; @ViewChild('scrollMe', { static: false }) divElement: ElementRef;
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes.logs && this.divElement && this.divElement.nativeElement) { if (changes.logs) {
setTimeout(() => { this.scrollToTheEnd();
this.divElement.nativeElement.scrollTop = this.divElement.nativeElement.scrollHeight;
});
} }
} }
@@ -24,4 +22,11 @@ export class XtermComponent implements OnChanges {
return this.logs.reduce((value, line) => value + line.message + '\n', ''); return this.logs.reduce((value, line) => value + line.message + '\n', '');
} }
scrollToTheEnd(): void {
setTimeout(() => {
if (this.divElement && this.divElement.nativeElement) {
this.divElement.nativeElement.scrollTop = this.divElement.nativeElement.scrollHeight;
}
});
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -49,7 +49,7 @@ export const initialState: State = {
log: [{ message: '', cssClass: XtermCssClass.standard }], log: [{ message: '', cssClass: XtermCssClass.standard }],
restoringUserConfiguration: false, restoringUserConfiguration: false,
hasBackupUserConfiguration: false, hasBackupUserConfiguration: false,
halvesInfo: { isLeftHalfConnected: true, areHalvesMerged: true } halvesInfo: { isLeftHalfConnected: true, areHalvesMerged: false }
}; };
export function reducer(state = initialState, action: Action): State { export function reducer(state = initialState, action: Action): State {

View File

@@ -446,16 +446,9 @@ export function reducer(
const userConfiguration: UserConfiguration = Object.assign(new UserConfiguration(), state.userConfiguration); const userConfiguration: UserConfiguration = Object.assign(new UserConfiguration(), state.userConfiguration);
userConfiguration.macros = state.userConfiguration.macros.map((macro: Macro) => { userConfiguration.macros = state.userConfiguration.macros.map((macro: Macro) => {
if (macro.id === payload.id) { if (macro.id === payload.id) {
let newIndex: number = payload.newIndex;
// We need to reduce the new index for one when we are moving action down
if (newIndex > payload.oldIndex) {
--newIndex;
}
macro = new Macro(macro); macro = new Macro(macro);
macro.macroActions.splice( macro.macroActions.splice(
newIndex, payload.newIndex,
0, 0,
macro.macroActions.splice(payload.oldIndex, 1)[0] macro.macroActions.splice(payload.oldIndex, 1)[0]
); );

View File

@@ -15,7 +15,7 @@ import { effects } from './store/effects';
BrowserModule, BrowserModule,
SharedModule, SharedModule,
StoreModule.forRoot(reducers), StoreModule.forRoot(reducers),
StoreRouterConnectingModule, StoreRouterConnectingModule.forRoot(),
StoreDevtoolsModule.instrument({ StoreDevtoolsModule.instrument({
maxAge: 10 maxAge: 10
}), }),

View File

@@ -8,4 +8,6 @@ if (environment.production) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic().bootstrapModule(UhkRendererModule); platformBrowserDynamic().bootstrapModule(UhkRendererModule, {
preserveWhitespaces: true
});

View File

@@ -8,4 +8,6 @@ if (environment.production) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic().bootstrapModule(WebModule); platformBrowserDynamic().bootstrapModule(WebModule, {
preserveWhitespaces: true
});

View File

@@ -22,7 +22,7 @@ import { effects } from '../app/store/effects';
SharedModule, SharedModule,
routing, routing,
StoreModule.forRoot(reducers), StoreModule.forRoot(reducers),
StoreRouterConnectingModule, StoreRouterConnectingModule.forRoot(),
StoreDevtoolsModule.instrument({ StoreDevtoolsModule.instrument({
maxAge: 10 maxAge: 10
}), }),

View File

@@ -3,7 +3,7 @@
"compilerOptions": { "compilerOptions": {
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"baseUrl": "./", "baseUrl": "./",
"target": "es6", "target": "es5", // Until https://github.com/electron/electron/issues/12011 not working without custom protocol
"types": ["node"], "types": ["node"],
"lib": [ "lib": [
"es2016", "es2016",

View File

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

View File

@@ -37,9 +37,9 @@
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
}, },
"base64-js": { "base64-js": {
"version": "0.0.8", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
}, },
"bindings": { "bindings": {
"version": "1.5.0", "version": "1.5.0",
@@ -68,20 +68,38 @@
} }
}, },
"buffer": { "buffer": {
"version": "3.6.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.0.tgz",
"integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", "integrity": "sha512-Xpgy0IwHK2N01ncykXTy6FpCWuM+CJSHoPVBLyNqyrWxsedpLvwsYUhf0ME3WRFNUhos0dMamz9cOS/xRDtU5g==",
"requires": { "requires": {
"base64-js": "0.0.8", "base64-js": "^1.0.2",
"ieee754": "^1.1.4", "ieee754": "^1.1.4"
"isarray": "^1.0.0"
} }
}, },
"buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
"requires": {
"buffer-alloc-unsafe": "^1.1.0",
"buffer-fill": "^1.0.0"
}
},
"buffer-alloc-unsafe": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
},
"buffer-crc32": { "buffer-crc32": {
"version": "0.2.13", "version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
}, },
"buffer-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
},
"chalk": { "chalk": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
@@ -93,9 +111,9 @@
} }
}, },
"chownr": { "chownr": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
@@ -103,11 +121,11 @@
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
}, },
"color-convert": { "color-convert": {
"version": "1.9.1", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": { "requires": {
"color-name": "^1.1.1" "color-name": "1.1.3"
} }
}, },
"color-name": { "color-name": {
@@ -116,9 +134,9 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
}, },
"commander": { "commander": {
"version": "2.15.1", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
@@ -255,9 +273,9 @@
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
}, },
"fd-slicer": { "fd-slicer": {
"version": "1.0.1", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
"requires": { "requires": {
"pend": "~1.2.0" "pend": "~1.2.0"
} }
@@ -272,6 +290,11 @@
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
}, },
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -307,9 +330,9 @@
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
}, },
"glob": { "glob": {
"version": "7.1.2", "version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"requires": { "requires": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
"inflight": "^1.0.4", "inflight": "^1.0.4",
@@ -320,9 +343,9 @@
} }
}, },
"graceful-fs": { "graceful-fs": {
"version": "4.1.11", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw=="
}, },
"graceful-readlink": { "graceful-readlink": {
"version": "1.0.1", "version": "1.0.1",
@@ -340,9 +363,9 @@
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
}, },
"ieee754": { "ieee754": {
"version": "1.1.11", "version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==" "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
}, },
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
@@ -354,9 +377,9 @@
} }
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -364,9 +387,9 @@
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
}, },
"interpret": { "interpret": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
"integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=" "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw=="
}, },
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
@@ -392,9 +415,9 @@
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
}, },
"make-dir": { "make-dir": {
"version": "1.2.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
"integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
"requires": { "requires": {
"pify": "^3.0.0" "pify": "^3.0.0"
}, },
@@ -450,17 +473,17 @@
"integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==" "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA=="
}, },
"node-abi": { "node-abi": {
"version": "2.8.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.8.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.11.0.tgz",
"integrity": "sha512-1/aa2clS0pue0HjckL62CsbhWWU35HARvBDXcJtYKbYR7LnIutmpxmXbuDMV9kEviD2lP/wACOgWmmwljghHyQ==", "integrity": "sha512-kuy/aEg75u40v378WRllQ4ZexaXJiCvB68D2scDXclp/I4cRq6togpbOoKhmN07tns9Zldu51NNERo0wehfX9g==",
"requires": { "requires": {
"semver": "^5.4.1" "semver": "^5.4.1"
} }
}, },
"node-hid": { "node-hid": {
"version": "0.7.8", "version": "0.7.9",
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.8.tgz", "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.9.tgz",
"integrity": "sha512-79Z9hw/pqIDp0kxvb353ivGgslo4i0hYQTcCqfRFxIJSO2gF9VtPla5uQY/9jTcDlON5O5YaqxbdH+8bs+m+1Q==", "integrity": "sha512-vJnonTqmq3frCyTumJqG4g2IZcny3ynkfmbfDfQ90P3ZhRzcWYS/Um1ux6HFmAxmkaQnrZqIYHcGpL7kdqY8jA==",
"requires": { "requires": {
"bindings": "^1.5.0", "bindings": "^1.5.0",
"nan": "^2.13.2", "nan": "^2.13.2",
@@ -517,9 +540,9 @@
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
}, },
"path-parse": { "path-parse": {
"version": "1.0.5", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
}, },
"pend": { "pend": {
"version": "1.2.0", "version": "1.2.0",
@@ -568,9 +591,9 @@
} }
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"pump": { "pump": {
"version": "2.0.1", "version": "2.0.1",
@@ -604,6 +627,13 @@
"safe-buffer": "~5.1.1", "safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1", "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1" "util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
} }
}, },
"rechoir": { "rechoir": {
@@ -615,17 +645,17 @@
} }
}, },
"resolve": { "resolve": {
"version": "1.7.0", "version": "1.12.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
"integrity": "sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA==", "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
"requires": { "requires": {
"path-parse": "^1.0.5" "path-parse": "^1.0.6"
} }
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.1", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}, },
"seek-bzip": { "seek-bzip": {
"version": "1.0.5", "version": "1.0.5",
@@ -646,9 +676,9 @@
} }
}, },
"semver": { "semver": {
"version": "5.7.0", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
@@ -712,6 +742,13 @@
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": { "requires": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
} }
}, },
"strip-ansi": { "strip-ansi": {
@@ -766,13 +803,16 @@
} }
}, },
"tar-stream": { "tar-stream": {
"version": "1.5.5", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
"integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
"requires": { "requires": {
"bl": "^1.0.0", "bl": "^1.0.0",
"buffer-alloc": "^1.2.0",
"end-of-stream": "^1.0.0", "end-of-stream": "^1.0.0",
"readable-stream": "^2.0.0", "fs-constants": "^1.0.0",
"readable-stream": "^2.3.0",
"to-buffer": "^1.1.1",
"xtend": "^4.0.0" "xtend": "^4.0.0"
} }
}, },
@@ -789,6 +829,11 @@
"os-tmpdir": "~1.0.2" "os-tmpdir": "~1.0.2"
} }
}, },
"to-buffer": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
"integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
},
"tunnel-agent": { "tunnel-agent": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -804,12 +849,12 @@
"dev": true "dev": true
}, },
"unbzip2-stream": { "unbzip2-stream": {
"version": "1.2.5", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz",
"integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==",
"requires": { "requires": {
"buffer": "^3.0.1", "buffer": "^5.2.1",
"through": "^2.3.6" "through": "^2.3.8"
} }
}, },
"util-deprecate": { "util-deprecate": {
@@ -836,17 +881,17 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"xtend": { "xtend": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
}, },
"yauzl": { "yauzl": {
"version": "2.9.1", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
"requires": { "requires": {
"buffer-crc32": "~0.2.3", "buffer-crc32": "~0.2.3",
"fd-slicer": "~1.0.1" "fd-slicer": "~1.1.0"
} }
} }
} }

View File

@@ -13,7 +13,7 @@
"commander": "^2.11.0", "commander": "^2.11.0",
"decompress": "^4.2.0", "decompress": "^4.2.0",
"decompress-tarbz2": "^4.1.1", "decompress-tarbz2": "^4.1.1",
"node-hid": "0.7.8", "node-hid": "0.7.9",
"shelljs": "^0.7.8", "shelljs": "^0.7.8",
"tmp": "0.0.33", "tmp": "0.0.33",
"uhk-common": "1.0.0", "uhk-common": "1.0.0",

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env ts-node
///<reference path="./node_modules/@types/node/index.d.ts"/>
import { UhkBuffer, UserConfiguration } from 'uhk-common'; import { UhkBuffer, UserConfiguration } from 'uhk-common';
import * as fs from 'fs'; import * as fs from 'fs';

View File

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

View File

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