330 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
Róbert Kiss
f6bef928fe chore(usb): add kboot-firmware-upgrade script (#1006) 2019-08-02 02:51:07 +02:00
László Monda
b691f866c5 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-08-01 23:48:43 +02:00
Róbert Kiss
5ab5ad5f0c feat: ESC cancel the keymap description editing (#1007) 2019-07-31 10:03:29 +02:00
László Monda
4e09f95653 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2019-07-30 22:24:00 +02:00
Róbert Kiss
4d2d6d40fb chore(kboot): add more logging (#1004) 2019-07-30 22:23:37 +02:00
Róbert Kiss
db2e14a852 chore: upgrade ts-node => 8.3.0 (#1003) 2019-07-29 20:56:01 +02:00
Róbert Kiss
757a201c47 chore: upgrade lerna => 3.16.4 (#998) 2019-07-28 23:30:35 +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
Róbert Kiss
c2c997c9b0 feat: decrease the size between the keyboard layout and description in split mode (#990)
* feat: decrease the size between the keyboard layout and description if split

* fix: increase the distance between the keyboard and keyboard desc.

* fix: increase the description distance in split mode
2019-07-28 20:43:12 +02:00
Róbert Kiss
0015e6deb3 chore: upgrade electron => 4.2.8 (#995)
Summary:
- 10.11.0 the bundled Node.js version in electron
- Chromium version number 69.0.3497.128

BREAKING CHANGES:
- require minimum MacOs version 10.10 Yosemite
2019-07-28 20:41:11 +02:00
Róbert Kiss
1134c1d16e chore: write kboot logs in development mode (#994) 2019-07-28 12:50:44 +02:00
Róbert Kiss
1b569aa82b chore: use fs-extra instead of mkdirp package (#992) 2019-07-28 12:32:30 +02:00
Róbert Kiss
66d6802920 chore: upgrade typescript => 3.5.3 (#993) 2019-07-28 12:31:37 +02:00
Róbert Kiss
bf1689dd34 chore: upgrade fs-extra => 8.1.0 (#989) 2019-07-26 12:33:43 +02:00
Róbert Kiss
f5e05f19b6 chore: upgrade ts-lib => 1.10.0 (#991) 2019-07-26 12:33:06 +02:00
Róbert Kiss
05b8fc89fe chore: remove autoprefixer
We don't use the package directly.
The @angular-devkit/build-angular@0.13.9 use the up to date version and
manage the css prefixing
2019-07-26 06:21:30 +02:00
Róbert Kiss
cbccaba1c5 feat: Display/hide left keyboard half and merged/unmerged state (#987)
* feat: Display/hide left keyboard half and merged/unmerged state

* feat: improve the animation

* feat: decrease left fade animation time
2019-07-25 23:41:20 +02:00
Róbert Kiss
a409c219d8 feat: Provide reasonable default mouse settings for Macs (#982)
* feat: Provide reasonable default mouse settings for Macs

* fix: hack to eliminate the double scrollbar
2019-07-25 22:02:52 +02:00
Róbert Kiss
2b84101975 chore: upgrade karma-coverage-istanbul-reporter => 2.0.6 (#986)
The new version uses newer version of the `handlebars` and it
protected `Prototype Pollution` vulnerability.
2019-07-23 18:41:13 +02:00
Róbert Kiss
1c551c0669 chore: upgrade tslint => 5.18.0 (#985) 2019-07-23 00:46:51 +02:00
Róbert Kiss
fe24ebbefc chore: upgrade lerna => 3.16.1 2019-07-23 00:22:51 +02:00
Róbert Kiss
a4cb4b7e0c style: remove extra space in user config reducer (#983) 2019-07-22 23:15:45 +02:00
Róbert Kiss
cba93a41e0 chore: upgrade lodash => 4.17.15 (#981) 2019-07-22 21:29:07 +02:00
Eric Tang
9652152225 Sign CLA (#979) 2019-07-21 23:07:37 +02:00
László Monda
ffcfce04d5 Make scripts terminate with a non-zero error code upon failure. 2019-07-20 13:04:11 +02:00
László Monda
7c6da6c4e6 Fix changelog. 2019-07-14 12:50:04 +02:00
László Monda
4c57f28a3c Update frimware version to 8.5.4 2019-07-14 12:48:38 +02:00
László Monda
4677829d56 Update changelog. 2019-07-14 12:45:16 +02:00
László Monda
e812b7dfcc Make the help page a bit tighter. 2019-07-14 12:44:47 +02:00
Róbert Kiss
0290dee35c chore: the correct windows code sign certificate 2019-07-07 19:47:38 +02:00
Sam Rang
3c6c12d7a8 Sign CLA (#977) 2019-07-05 21:16:50 +02:00
Róbert Kiss
01aa9f119c chore: update windows code sign certificate (#976) 2019-07-03 22:54:37 +02:00
Kristian Sloth Lauszus
2850658055 Sign CLA (#975) 2019-07-02 02:44:32 +02:00
Spencer Owen
c1e295c92d I have read the Agreement, and fully agree to it by signing it with my GitHub username. (#964) 2019-06-22 09:10:40 +02:00
Mikko Lakomaa
40d29886cb Sign CLA (#971) 2019-06-18 22:24:19 +02:00
Árpád Csányi
a02c4deae1 Sign CLA. (#972) 2019-06-18 22:11:39 +02:00
József Farkas
c1b7078549 Sign CLA (#970) 2019-06-13 22:50:43 +02:00
László Monda
293affacb3 Sort signatures alphabetically. 2019-06-13 11:00:40 +02:00
dgyimesi
c41450d0dd Sign CLA. (#969) 2019-06-13 10:57:03 +02:00
László Monda
a9ec8efb2b Sort signatures alphabetically. 2019-06-13 00:37:32 +02:00
Tim Coker
d7ecd5a083 sign-cla (#968) 2019-06-13 00:36:07 +02:00
László Monda
8c4b13cd94 Sort names alphabetically. 2019-06-12 20:12:33 +02:00
Róbert Kiss
b4ec69ff6d chore: ert78gb sign cla (#966) 2019-06-12 20:11:52 +02:00
László Monda
62f69b5f70 Alphabetically sort signatures. 2019-06-12 17:59:03 +02:00
Attila Csanyi
e2fda9201f Sign CLA (#965)
I have read the Agreement, and fully agree to it by signing it with my GitHub username.
2019-06-12 17:58:18 +02:00
Nejc Zdovc
bf9bfb8d82 CLA Nejc Zdovc (#963) 2019-06-10 12:43:15 +02:00
László Monda
02089d166c Update CONTRIBUTING.md 2019-06-07 21:43:56 +02:00
László Monda
3df6e8214b Update CONTRIBUTING.md 2019-06-07 21:41:55 +02:00
László Monda
fbc8970baa Update CONTRIBUTING.md 2019-06-07 21:40:13 +02:00
László Monda
88b3b2fb65 Sign CLA. (#962) 2019-06-07 21:31:09 +02:00
László Monda
41f73acfce Add CLA. 2019-06-07 21:20:00 +02:00
Róbert Kiss
9d41f40d93 chore: use the correct license entry in the package.json of the uhk-web (#960) 2019-06-02 22:21:29 +02:00
Róbert Kiss
56ebfba8ad chore: move @types/* dependencies to the root package.json (#961) 2019-06-02 22:21:05 +02:00
Róbert Kiss
400f01dabe chore: upgrade @angular/cli => 7.3.9 and devkit => 0.13.9 (#959) 2019-05-31 12:36:27 +02:00
Róbert Kiss
c64515885b chore: upgrade electron => 3.1.10 (#958) 2019-05-30 01:35:18 +02:00
Róbert Kiss
82c9126d82 refactor: use Buffer.from and Buffer.alloc instead of new Buffer() (#957) 2019-05-29 21:56:01 +02:00
Róbert Kiss
999feea488 chore: upgrade angular => 7.2.15 and ngrx => 7.4.0 (#956) 2019-05-29 20:28:29 +02:00
László Monda
4dd2400d36 Add a 1s delay to updateDeviceFirmware() 2019-05-23 01:26:32 +02:00
László Monda
5657579d6d Add a 1s delay to updateModuleFirmware() 2019-05-23 01:10:54 +02:00
Róbert Kiss
58b83f2b6b chore: upgrade jasmine* => 3.4.0 (#953) 2019-05-23 00:35:53 +02:00
Róbert Kiss
b991e2c9d4 chore: upgrade lerna => 3.14.1 (#952) 2019-05-23 00:00:45 +02:00
Róbert Kiss
e6495ded05 chore: upgrade node-hid => 0.7.8 (#951) 2019-05-22 23:27:40 +02:00
László Monda
21cab7e755 Revert "Rebuild node-hid for the usb repo to avoid "dev 1d50 / 6122 : usage_page:312d, usage:2e30" messages when using USB scripts."
This reverts commit 2dfb61a38e.
2019-05-22 21:23:23 +02:00
László Monda
2dfb61a38e Rebuild node-hid for the usb repo to avoid "dev 1d50 / 6122 : usage_page:312d, usage:2e30" messages when using USB scripts. 2019-05-22 18:09:36 +02:00
László Monda
2c93975c77 Rename {left,right}-addon to {left,right}-module 2019-05-15 23:41:42 +02:00
László Monda
427f706287 Rename {left,right}Addon to {left,right}Module 2019-05-15 23:35:05 +02:00
Róbert Kiss
1b59d96296 fix: don't show the udev setup info if udev v2 setup available (#943) 2019-04-30 03:05:03 +02:00
Róbert Kiss
20e2c8bbbc chore: upgrade jQuery => 3.4.0
fix: https://nvd.nist.gov/vuln/detail/CVE-2019-11358
2019-04-28 17:06:59 +02:00
László Monda
c2f47f18b0 Link the UHK knowledgebase from the help page. 2019-04-24 17:00:15 +02:00
Tim Coker
1ee31003fd fix for middle clicking links (#937)
* fix for middle clicking links

* Found another link in the app

* Converted all links to use externaUrl decorator

* Missing semicolon

* Minor fixes for compilation/linting

* Changed help link back to embedded in html

* trigger builds

* trigger build for agreement removal
2019-04-24 16:09:32 +02:00
Róbert Kiss
4abdac1ef6 fix: delete LayerSwitcher on the dest layer when LayerSwitcher remove/changing (#939) 2019-04-18 01:58:16 +02:00
Róbert Kiss
32db3817e2 chore: upgrade stylelint to 10.0.0 (#938)
* chore: upgrade stylelint to 10.0.0

* Trigger build
2019-04-16 22:11:11 +02:00
Róbert Kiss
264752530f chore: upgrade bootstrap to 3.4.1 (#928)
* chore: upgrade bootstrap to 3.4.1

* fix: add whitelist to the toolbox sanitizer

* fix: remove unnecessary console.log
2019-04-16 21:02:13 +02:00
Róbert Kiss
33c910d70c chore: upgrade angular => 7.x (#925)
* Merge branch 'master' into chore-upgrade-angular-to-7

* reformat files of the store

* set preserveWhitespaces = true

* delete console.log from AutoGrowInputComponent

* fix null pinter exception when set the keyaction on an undefined key

* speed tuning

* delete svg-keyboard-key animation

* revert electron logger upgrade

* improve animation speed of scg-keyboard-key component

* fix: popover keymap tab visibility

* fix: remove btn-line css class
2019-03-04 10:27:25 +01:00
Róbert Kiss
b1b2f1d431 chore: remove standard-version from devDependencies (#924)
We don't use standard-version to generate changelog
2019-02-28 19:16:27 +01:00
Skye Jensen
c3a6b373d4 Update lerna dependency from 3.2.0 to 3.13.0 (#923) 2019-02-27 21:11:42 +01:00
Róbert Kiss
f94b221ee9 feat: convert UdevRulesInfo to string enum (#921)
* feat: convert UdevRulesInfo to string enum

For easier log reading better to use string enum.

* fix typo
2019-02-24 22:04:46 +01:00
Róbert Kiss
3a1451a6a9 fix: agent falsely reports UHK detected (#922)
close #915
2019-02-24 19:48:36 +01:00
Róbert Kiss
7a2c8cb2e4 refactor: ngrx-store use enum ActionTypes and Action classes everywhere (#919) 2019-02-22 19:00:17 +01:00
Róbert Kiss
de71f6f88c chore: upgrade lodash (#914) 2019-02-21 11:18:13 +01:00
Róbert Kiss
5fd555c214 refactor: remove console.log from popover component (#911) 2019-02-02 02:12:20 +01:00
Róbert Kiss
9a0b0f9df1 feat: redesign auto-update component (#910) 2019-02-02 02:11:54 +01:00
Róbert Kiss
12d361eb2e feat: save window state when closing agent (#902) 2019-01-30 02:47:23 +01:00
dgyimesi
2c041b64ff feat: Redesign About page. (#899)
* Redesign About page.

* feat: migrate About page to ngrx.

* Fix import relative path; order contributors by the number of contributions.
2019-01-27 00:43:57 +01:00
dgyimesi
8947f2dedf feat: Make the Agent application icon slightly smaller. (#898)
* feat: Make the Agent application icon slightly smaller.

* feat: generate icon.icns
2019-01-26 22:51:13 +01:00
Róbert Kiss
6b4cf41357 chore: upgrade gh-pages => 2.0.1 (#907)
fix: https://nodesecurity.io/advisories/658
2019-01-26 21:46:56 +01:00
dgyimesi
c36156b497 feat: Make Right click, Esc set the key action to none (#897)
* feat: Make Right click, Esc set the key action to none

* refactor: use ts-keycode-enum; revert: escape from mapping on pressing escape.
2019-01-24 14:25:22 +01:00
Róbert Kiss
d674fd4490 chore: upgrade electron => 2.0.16 (#903) 2019-01-24 03:22:12 +01:00
Róbert Kiss
8acc33e719 chore: set preserveWhitespaces: false in angular tsconfig.json (#904) 2019-01-24 01:41:52 +01:00
Mónus Tamás
2855480d6a fix: Pressing Tab after "update description" corrupts UI (#872) (#878)
* fix: Pressing Tab after "update description" corrupts UI (#872)

* Prevent focus on button groups by setting tabindex to -1.
2019-01-21 20:09:06 +01:00
László Monda
eee3322082 Use TAG+="uaccess" instead of MODE:="0666", GROUP="plugdev" in the udev rules for better compatibility. Fixes #901 2019-01-21 14:47:17 +01:00
Róbert Kiss
bb31c2cefa refactor: use rxjs pipe syntax (#900)
The `let` operator was not migrated because earlier two reducer needed be refactored
- user-configuration reducer
- present reducer

This commit is prerequisite of the angular upgrade.
2019-01-20 23:23:01 +01:00
dgyimesi
e18a98d8bb fix: Don't change the tab immediately when closing the key action popover (#895) 2019-01-20 22:55:06 +01:00
Róbert Kiss
3964698cf7 feat: kboot package (#894)
* feat: kboot package

* feat: kboot package

* fix: wait 1 sec after device is available

* test: fix unit test

* refactor: clean unused codes

* doc: improve readme.md

* doc: improve readme.md

* test: fix unit test

* chore: fix lint settings

* style: fix linting issues
2019-01-18 17:37:31 +01:00
Róbert Kiss
bfc08edfce chore(release): 1.2.13 2019-01-07 21:56:23 +01:00
Róbert Kiss
6e2115ac74 feat: kboot package (#889) 2019-01-07 21:36:36 +01:00
Róbert Kiss
1eb8720305 chore: reorganize npm script (#884)
The goal is every package should be responsible to `lint`, `build` and
`test` commands. This modification helps to easier add new package to
the repository.
2018-12-22 02:20:38 +01:00
Róbert Kiss
c9f052b8c7 chore: delete unused webpack.config.js file in test-serializer package (#882) 2018-12-22 00:52:43 +01:00
Róbert Kiss
58ee42fcc2 chore: unify tsconfig.json files (#883) 2018-12-22 00:46:17 +01:00
Róbert Kiss
9112b597f8 fix: device recovery mode (#879) 2018-12-18 00:10:02 +01:00
Róbert Kiss
8c7d625573 build: set publisherName for win release 2018-11-15 22:33:22 +01:00
Róbert Kiss
52a57c0e87 build: upgrade electron-builder and remove unsupported appMetadata (#860) 2018-11-14 23:40:28 +01:00
Róbert Kiss
a52b34fc3f Revert "chore: rebuild node-hid to hide USB usage data (#853)" (#861)
This reverts commit 1a14ac020e.
2018-11-14 23:14:05 +01:00
Róbert Kiss
1a14ac020e chore: rebuild node-hid to hide USB usage data (#853) 2018-11-14 22:22:24 +01:00
László Monda
330f7e72be Bump Agent version to 1.2.12 and update changelog. 2018-11-14 19:57:35 +01:00
Róbert Kiss
1ed6669ced fix: compare available devices by vid, pid, interface on linux (#854) 2018-11-13 21:44:22 +01:00
Róbert Kiss
108d60a497 feat: show os specific modifiers for macro actions (#855) 2018-11-12 23:22:26 +01:00
Róbert Kiss
1a9bd7de83 feat: show udev rules on missing device screen (#842)
* feat: show udev rules on missing device screen

* delete MissingDeviceComponent

* feat: change privilege and message device screen texts

* feat: change message device screen texts

* fix: load config from device

* fix: load config when hasPermission = true

* fix: load app start info after set permission rules

* fix: action dispatch

* fix: load config from device when UdevRulesInfo.Ok

* fix: load config from device when 0 device available

* feat: add extra space between the "old udev rule" and permission button
2018-11-09 01:30:40 +01:00
Róbert Kiss
10cd06c70b feat: allow 8.4.3 firmware features (#845)
* feat: allow 8.4.3 firmware features

* feat: remove "layer-double-tap" command line argument
2018-11-05 19:51:39 +01:00
László Monda
84b6c33c54 Upgrade to firmware 8.5.3 2018-10-30 04:02:24 +01:00
Róbert Kiss
18808eae9c fix: user Renderer2 instead of deprecated Renderer (#844) 2018-10-30 03:06:44 +01:00
Róbert Kiss
ca74b0a76b feat: log USB device list before check permission (#837) 2018-10-22 21:32:16 +02:00
Róbert Kiss
b45d60efb2 feat: log USB device list before check permission (#837) 2018-10-22 07:34:14 +02:00
Richard Prillwitz
a4e3696078 chore: upgrade dependencies (#828)
* fixed vulnerabilities;

* update lerna from 3.1.4 to 3.2.0;

* fixed linter complaining about two empty lines in scss file;

* use fixed versions,
2018-10-16 19:17:12 +02:00
Róbert Kiss
2b963993d2 fix: stop event propagation when capturing keypress action (#817)
* fix: stop event propagation when capturing keypress action

* fix: save to keyboard shortcut allowed when keypress not capturing
2018-10-14 20:43:35 +02:00
László Monda
e333022043 Add international scancodes. Fixes #807. 2018-10-09 23:02:07 +02:00
Róbert Kiss
2e2a59ccb8 feat: remove Stop/Eject keypress action (#819)
* feat: remove Stop/Eject keypress action

* feat: remove WWW and Launch Web Browser keypress actions
2018-10-08 02:35:48 +02:00
Róbert Kiss
6f073ad718 feat: use icon for play/pause keypress action (#818)
* feat: use icon for play/pause keypress action

* fix: use rectangle layout in svg-single-icon-key
2018-10-08 02:32:02 +02:00
László Monda
166834e46c Update udev rules based on the instructions of https://github.com/node-hid/node-hid/releases/tag/v0.7.2 2018-10-08 00:22:14 +02:00
László Monda
0ff2364b9e Update from firmware 8.2.5 to 8.5.2 2018-10-08 00:00:08 +02:00
Róbert Kiss
2cbfc6a11e fix: ui break when press tab key in keymap short name control (#816) 2018-10-07 23:21:25 +02:00
Róbert Kiss
404ccc7b2b chore: use default electron menu on mac in development mode (#815) 2018-10-07 22:42:39 +02:00
Róbert Kiss
42e413ab65 chore: upgrade node-hid => 0.7.3 (#814) 2018-10-07 22:29:10 +02:00
Róbert Kiss
d57ef66038 feat: disable other popover tabs if the current key is layer switcher action (#813) 2018-10-07 22:14:36 +02:00
Róbert Kiss
843b4cbf68 feat: add "Edit" menu to MacOs build (#812) 2018-10-07 21:02:28 +02:00
Róbert Kiss
7e4b7c5c8b fix: macro action text overlap (#811) 2018-10-07 18:31:15 +02:00
László Monda
2ff65537a0 Update ISSUE_TEMPLATE 2018-10-06 20:28:13 +02:00
László Monda
6e2b1fb18d Add usbReportSemaphore to uhk.variableNametoId 2018-10-04 19:27:19 +02:00
László Monda
3e621a2818 Bump version to 1.2.11 and update package.json 2018-10-03 05:43:41 +02:00
Róbert Kiss
247ec4c1b2 feat: add backspace and caps lock icons (#803)
Summary:
- svg sprite generate with xmlns:xlink="http://www.w3.org/1999/xlink" namespace
- uhk-icon-agent-icon css class renamed to uhk-icon-pure-agent-icon because
  it collided with agent icon sprites
- added backspace and caps lock icons
2018-10-01 02:06:23 +02:00
Róbert Kiss
edcff069fd fix: write agent version into the log when upgrade firmware (#802) 2018-09-30 22:48:42 +02:00
László Monda
8afdeac306 Fix right and middle mouse click macro actions which were exchanged. Fixes #794. 2018-09-27 00:35:13 +02:00
László Monda
425f861451 Add issue template regarding Karabiner Elements. 2018-09-26 00:05:14 +02:00
László Monda
5a843ed02c Include Agent version to the firmware update log. 2018-09-25 15:48:27 +02:00
László Monda
f3bd83af03 Bump version to 1.2.10, update changelog and package.json 2018-09-24 04:21:24 +02:00
László Monda
0b3fca63b7 Add History Back and History Forward scancodes. Resolves #776. 2018-09-24 03:56:29 +02:00
László Monda
cbd4460df0 Map Caps Lock without Ctrl on default keymaps. 2018-09-24 03:03:05 +02:00
László Monda
b941bd9a75 Set the decelerated scroll speed of the default configuration to 10. 2018-09-24 02:32:25 +02:00
László Monda
439b84affc Save decelerated mouse scroll speed properly instead of using the accelerated scroll speed by accident. Fixes https://github.com/UltimateHackingKeyboard/firmware/issues/178 2018-09-24 02:21:00 +02:00
László Monda
66d5302e6f Add play/pause icon. Improves #591. 2018-09-21 01:43:15 +02:00
László Monda
9e2e2b9c5c Add more exact instructions to the permission setup screen. Fixes #781. 2018-09-21 00:56:50 +02:00
Róbert Kiss
aa243ac7b0 feat: allow Layer Switcher secondary roles only on base layer (#790) 2018-09-21 00:20:46 +02:00
László Monda
eb421e0681 Revert "Remove unused mustache file."
This reverts commit 63aae8f578.
2018-09-20 02:31:06 +02:00
László Monda
63aae8f578 Remove unused mustache file. 2018-09-20 02:08:58 +02:00
László Monda
e577454a31 Make all SVGs non-executable. 2018-09-20 02:05:51 +02:00
László Monda
e802bb0052 Remove unused SVGs. 2018-09-20 02:05:15 +02:00
László Monda
b98e5df20a Add backspace and caps lock icons. Improves #83. 2018-09-20 01:42:05 +02:00
Róbert Kiss
7332105edb feat: remap on all keymap warning (#789) 2018-09-19 23:02:32 +02:00
László Monda
6a4feaf18d Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-09-16 21:30:58 +02:00
László Monda
ee637d7958 Rename Scroll Lock to ScrLk and Num Lock to NumLk on keys. Improves #83. 2018-09-16 21:28:54 +02:00
Róbert Kiss
8d161ce8ff chore: upgrade Node.js => 8.12.0 (#783)
it's fix appveyor build
2018-09-16 21:17:40 +02:00
László Monda
8010bd8195 In the scancode select2 display "Print Screen SysRq" and add SysRq above PrtScn when rendering the key. Fixes #784. 2018-09-16 21:09:54 +02:00
Jan Christoph Ebersbach
059f1d5505 Fix swapped direction title for mouse movements (#778) 2018-09-16 01:16:11 +02:00
László Monda
123cab5724 Bump Agent version to 1.2.9 2018-09-13 20:42:07 +02:00
Róbert Kiss
c16365a0e5 fix: Alt+Tab triggers "Remap on all layers" (#773) 2018-09-11 22:29:30 +02:00
Róbert Kiss
a21d278c0c fix: modifier button layout (#772) 2018-09-11 21:56:30 +02:00
Róbert Kiss
0466916be1 fix: key modifier reordering (#771) 2018-09-11 00:29:22 +02:00
László Monda
9a845d8f6a Fix eeprom.js 2018-09-10 19:29:58 +02:00
Róbert Kiss
9ae1673499 feat: secondary role visualisation (#767)
* feat: secondary role visualisation

* fix: recalculate the text position of the secondary role if changes

* fix: recalculate the text position of SvgKeystrokeKeyComponent

* fix: recalculate the text position when changes anything

* fix: two line text key position calculation

* fix: fix space positioning

* fix: visualize second character of complex key

* style: remove extra line
2018-09-08 12:09:16 +02:00
László Monda
2d5a5e7aef Exchange Alt and Super modifiers in the key action popover. 2018-09-03 03:58:03 +02:00
Róbert Kiss
3e4d439852 feat: display OS-specific modifiers (#764)
* chore: git ignore "out-tsc/" folder in uhk-web package

* feat: add OperationSystem calculation to the app reducer

* feat: create os specific key modifier

* feat: Os specific texts

* revert: KeyModifierValues and getKeyModifiers selector

* refactor: remove unnecessary return

* refactor: rename OperationSystem => OperatingSystem
2018-09-03 00:21:55 +02:00
László Monda
aba0b09109 Bump Agent to version 1.2.8 and update changelog. 2018-08-26 23:42:31 +02:00
László Monda
af608ee17d Downgrade to firmware 8.2.5 2018-08-26 23:35:54 +02:00
Róbert Kiss
df817e86d6 fix: use OnPush change detection on the keymap edit and add pages (#760)
* fix: use OnPush change detection on the keymap edit and add pages

* fix: font-size
2018-08-26 22:08:29 +02:00
Róbert Kiss
a7d3b62512 chore: upgrade many dependencies and restructure tsconfigs (#757)
* chore: upgrade many dependencies and restructure tsconfigs

Summary:
- upgrade many dependencies
- remove dev dependencies from test-serializer and uhk-common
- created root tsconfig.json and test-serializer and uhk-common
  tsconfigs extends it
- fixed e2e test

* chore: upgrade more dependencies
2018-08-22 00:34:16 +02:00
Róbert Kiss
b41f14192a chore: upgrade stylelint => 9.5.0 (#756)
it is fix "Unknown CSS word" error in IntelliJ
2018-08-21 20:43:18 +02:00
Róbert Kiss
475ec71983 fix: uncheck 'Remap on all keyboard' and 'Remap on all layer' checkbox by default (#754)
* fix: uncheck 'Remap on all keyboard' and 'Remap on all layer' checkbox by default

* fix: popover checkbox checked state
2018-08-21 20:41:54 +02:00
László Monda
b5cff2fa93 Fix the padding of the secondary role tooltip. 2018-08-14 04:10:30 +02:00
László Monda
80e8c014ec Bind left and right Shift on the Mouse layer of all keymaps in the default configuration. 2018-08-08 17:00:32 +02:00
László Monda
67d42f666c Simplify the output of get-debug-info.js 2018-08-04 02:29:39 +02:00
László Monda
fa32f95438 Add note to the LED brightness page saying that current UHK versions are not backlit. Also include spacing-bootstrap-3 2018-07-30 00:05:04 +02:00
László Monda
06878dd56a Remove the redundant scrollbar from the LED brightness page. 2018-07-29 00:28:34 +02:00
Róbert Kiss
374f6a3e6e chore: downgrade electron builder => 20.14.7 (#741)
* chore: downgrade electron builder => 20.14.7

* chore: downgrade electron builder => 20.8.1
2018-07-26 23:30:20 +02:00
László Monda
0b9c804a3d Bump Agent version to 1.2.7 and update changelog. 2018-07-26 22:20:28 +02:00
László Monda
365a459d61 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-07-26 22:16:33 +02:00
László Monda
cec891a2c0 Change the shortcut which enables the USB stack test code, so that it can be triggered with the default Mac US keymap. 2018-07-26 22:15:21 +02:00
Róbert Kiss
8eb8aa3032 chore: upgrade electron builder => 20.26.0 (#740) 2018-07-26 22:06:32 +02:00
László Monda
38184e7968 Bump Agent version to 1.2.6 and firmware version to 8.4.0. Update changelog. 2018-07-26 06:04:47 +02:00
Róbert Kiss
f6092ea195 fix: popover components use OnPush change detection (#727)
* fix: popover components use OnPush change detection

* fix: select2 selection bug

* chore: upgrade @ert78gb/ngx-select-ex => 3.7.0
2018-07-26 05:37:58 +02:00
László Monda
ac7d66e338 Add keyboard shortcut for enabling the USB stack test mode of the firmware. Resolves #735. 2018-07-26 05:34:48 +02:00
László Monda
b82a1da92a Remove usbVariables in favor of variableNameToId. 2018-07-25 11:44:57 +02:00
László Monda
b8859f7b64 Make {get,set}-variable.js expect variable name and use async/await. 2018-07-25 02:08:36 +02:00
László Monda
a04fa67446 Comment out the Settings menu until auto update is implemented. 2018-07-24 16:40:34 +02:00
Eric Tang
ac89aff018 Add scripts for getting/setting firmware variables (#734) 2018-07-22 16:07:21 +02:00
Róbert Kiss
e7cf8dc966 fix: no scroll when macro tab selected on popover (#731) 2018-07-17 22:08:45 +02:00
Róbert Kiss
d0102f5bdb feat: add help page (#728)
* feat: add help page

* feat: add help page content
2018-07-16 23:05:41 +02:00
László Monda
eb0daadf98 Overwrite the blhost binary with a statically compiled version that doesn't use special instructions.
See https://github.com/UltimateHackingKeyboard/agent/issues/681#issuecomment-403057795
2018-07-14 14:43:11 +02:00
László Monda
49d6ca173d Remove the blhost-old x86-64 Linux blhost binary. 2018-07-14 14:41:53 +02:00
László Monda
a3eb6a6b7e Fix typo. 2018-07-11 14:48:28 +02:00
László Monda
144ed57b20 Fix epic typo. 2018-07-08 14:44:41 +02:00
Róbert Kiss
6086ddabf0 build: build only AppImage for Linux (#719)
upgrade:
 - electron-builder => 20.15.0
 - electron-log => 2.2.16
 - electron-rebuild => 1.8.1
2018-07-08 14:34:38 +02:00
Róbert Kiss
84f378a276 feat: add save to keyboard and remap shortcut keys (#712)
* feat: add save to keyboard and remap shortcut keys

* feat: Alt and Shift keys set the remapOnAllKeymap and remapOnAllLayer

* fix: control + enter trigger remap keymap
2018-07-08 14:31:48 +02:00
Róbert Kiss
648e8d5f2c feat: keep current selected layer when changing keymap (#714) 2018-07-05 23:58:44 +02:00
Róbert Kiss
15df8d7129 WIP feat: replace ng2-select2 => ngx-select-ex (#706)
* feat: replace ng2-select2 => ngx-select-ex

* feat: style the ngrx-select

* feat: replace secondary role select2

* feat: replace Select2OptionData => SelectOptionData

* feat: replace select2 => ngx-select in macro-tab component

* feat: replace select2 => ngx-select in keymap-tab component

* feat: fix styles

* chore: remove select2 from dependencies

* fix: macro editor overflow

* fix: set the same font size for the toggle button

* fix: overflow

* chore: use @ert78gb/ngx-select-ex version of ngx-select-ex
2018-07-02 23:44:39 +02:00
Sylvain Benner
cfc0af9655 Fix Sleep key for macOS users in Mac keymaps and Fn layers (#707)
Reference: https://support.apple.com/en-us/HT201236
2018-06-29 12:19:59 +02:00
László Monda
f02e3181a6 Improve the phrasing of the firmware update error message. 2018-06-28 18:49:01 +02:00
Róbert Kiss
3d59bcf97e feat: Tweak unsupported Windows firmware update notification (#705)
* feat: Tweak unsupported Windows firmware update notification

* feat: Display firmware update status

* feat: throw error when left half not connected under firmware upgrade
2018-06-28 18:41:16 +02:00
László Monda
5e4fc983fb Rename the "Remap Key" button to "Remay key". 2018-06-27 21:28:16 +02:00
László Monda
32d9635b34 Tone down the color of the separator line. 2018-06-26 06:05:03 +02:00
László Monda
3978011d2e Bump Agent version to 1.2.5 and update changelog. 2018-06-26 03:13:49 +02:00
László Monda
cd1952a7df Restore blhost as blhost.old from 0f24427628 2018-06-26 02:21:33 +02:00
László Monda
4251477451 Comment out the export keymap icon because there isn't a way to import keymap yet, so it's useless. 2018-06-26 01:53:04 +02:00
Róbert Kiss
873f1de1ef fix: cancelling the key action popover holds its state (#699) 2018-06-25 00:27:14 +02:00
Róbert Kiss
150f993e5f feat: change side menu Agent icon (#698) 2018-06-25 00:05:01 +02:00
Róbert Kiss
06e76e5e0f fix: only flash the remapped key (#697) 2018-06-24 23:39:32 +02:00
Róbert Kiss
a208a264c7 fix: only animate keyboard separator when splitting (#696) 2018-06-24 23:06:33 +02:00
Róbert Kiss
114014fa13 feat: Show not supported OS on firmware page when relevant (#695) 2018-06-24 22:16:00 +02:00
Róbert Kiss
94cfd9d2e9 fix: SwitchKeymapAction not allow to refer to itself (#694) 2018-06-24 20:32:32 +02:00
Róbert Kiss
0aa9c73b4b feat: log firmware version before upgrading firmware (#693) 2018-06-24 19:56:11 +02:00
Róbert Kiss
5234f85dbe fix: close device when any error occurred in the communication (#692) 2018-06-24 18:59:13 +02:00
László Monda
bd8a2f704f Bump version to 1.2.4 and update changelog. 2018-06-21 18:23:49 +02:00
László Monda
439886d69f Replace Linux x86-64 blhost with a statically linked version that should run fine on every Linux distro. 2018-06-21 18:10:55 +02:00
László Monda
b2a37795e3 Add agent-logo-with-text.svg 2018-06-19 23:54:03 +02:00
László Monda
440db56080 Bump Agent version to 1.2.3 and update changelog. 2018-06-19 03:07:26 +02:00
László Monda
337e6e6bb6 Add example to the secondary role tooltip. 2018-06-19 02:44:44 +02:00
László Monda
a1aeda3d35 Further tweak the scancode tooltip. 2018-06-19 01:26:20 +02:00
László Monda
c6a83f8c9b Merge branch 'feat-tooltip-width' 2018-06-19 00:04:14 +02:00
László Monda
0f24427628 Replace the Linux x64 blhost binary with @iprok's version that seems to be more stable. 2018-06-18 23:53:51 +02:00
Róbert Kiss
f52dc36a6a feat: allow wider tooltip width than parent container (#682) 2018-06-18 23:37:15 +02:00
Róbert Kiss
63a936968d feat: allow wider tooltip width than parent container 2018-06-18 23:31:34 +02:00
László Monda
cabfde7963 Tweak the text of the scancode tooltip. 2018-06-18 23:26:06 +02:00
László Monda
79628c2351 Tweak the text of the key remap tooltip. 2018-06-18 22:49:48 +02:00
Róbert Kiss
762fa6f8bf fix: remap SwitchLayerAction 2018-06-18 19:50:34 +02:00
László Monda
a258c097a9 Downgrade to firmware 8.2.5 because it's the latest recommended version that doesn't contain annoying bugs. 2018-06-18 19:14:38 +02:00
László Monda
41faa98fcd Update the firmware update instructions and URL. 2018-06-16 18:22:01 +02:00
Róbert Kiss
c4d7318686 chore: make firmware update log shorter (#675)
* chore: add lodash to the roor package.json

* chore: make firmware update log shorter
2018-06-13 10:07:40 +02:00
Róbert Kiss
9ef11eaa34 feat: add keyboard separator line (#673)
* fix: blhost wrapper return with Promise.reject if error code !== 0

* feat: add keyboard separator line
2018-06-13 00:39:14 +02:00
Róbert Kiss
f34cb2df56 feat: remap key on all keymaps / layers (#668)
* add: popover checkboxs

* feat: add KeyActionRemap

* fix: template driven form checkbox name

* fix: delete key action only if it SwitchLayerAction

* feat: use remap on all keymaps/layers checkbox values in SAVE_KEY action

* feat: set default value to the remapOnAllKeymap and remapOnAllLayer checkbox

* fix: layer mapping
2018-06-10 21:50:49 +02:00
Róbert Kiss
83b9f0d1e9 fix: blhost wrapper return with Promise.reject if error code !== 0 (#669) 2018-06-07 23:38:27 +02:00
Róbert Kiss
7d81cf0c6a style: fix tslint error in switch-layer-action.ts (#666) 2018-06-07 22:54:20 +02:00
tenteen
82b76a9455 Fix left half timeout during fw update (#567) (#626)
The snooze call is skipped in the try block when the command throws an
exception.  As a result, the command tries maxTry times as fast as
possible.

The fix is to move the snooze call outside of the try block.

PS: #GotMyUHK
2018-06-07 22:50:18 +02:00
Róbert Kiss
4ae577f936 feat: make double tap to hold layer optional per key (#662)
* feat: make double tap to hold layer optional per key

* test: fix test serializer

* fix: remove "application start" text

* Add double-tap.svg

* Add closing dot at the end of the sentence.

* fead: add double-tap icon

* Bundle firmware version 8.3.0

* feat: 'layer-double-tap' feature flag

* feat: convert SwitchLayerMode to string enum
2018-06-07 22:11:41 +02:00
László Monda
81a83994ab Make the Export device configuration button show up as a primary button. 2018-05-28 15:58:17 +02:00
László Monda
1d3a3c7f5f Fix changelog. 2018-05-27 20:04:48 +02:00
László Monda
8bb645125d Update changelog and re-release v1.2.2 2018-05-27 02:53:10 +02:00
László Monda
9471b31a5d Instruct the user to power cycle the keyboard when encountered with an invalid hardware configuration. 2018-05-27 02:43:12 +02:00
László Monda
ffa52757c9 Upgrade to firmware 8.2.5 2018-05-27 02:31:22 +02:00
Róbert Kiss
ee53a0df9b feat: set disable style of device name in side menu (#656)
* feat: visualise disabled state of the 'Device name' input control

* fix: original value handling
2018-05-27 02:30:41 +02:00
Róbert Kiss
8e20c85e07 feat: add file upload component (#655)
The component allow upload the same file multiple times
2018-05-26 22:46:41 +02:00
László Monda
65ea786358 Don't include os.platform into the firmware update log because os.type makes it practically redundant. 2018-05-26 17:13:10 +02:00
László Monda
1035837b3b Fix lint error. 2018-05-22 15:29:48 +02:00
Róbert Kiss
18fc2e6b3f fix: permission detection when device not plugged (#653) 2018-05-22 15:11:43 +02:00
László Monda
fc728697d7 Bump version to 1.2.2 and update changelog and package.json 2018-05-22 02:19:48 +02:00
László Monda
cdf3caee9e Display device list at the beginning of the firmware update process. 2018-05-22 01:53:14 +02:00
László Monda
0a4d3a002e Include operating system type to the firmware update log. Add a new tip to the firmware page. 2018-05-22 01:16:02 +02:00
László Monda
d11c532ea4 Add a lot of useful instructions to the firmware page. 2018-05-22 00:38:27 +02:00
László Monda
1ff51697b1 Upgrade from firmware 8.2.2 to 8.2.4 2018-05-21 14:34:42 +02:00
Róbert Kiss
ab8ae31324 fix: permission detection on linux (#651) 2018-05-21 11:46:19 +02:00
László Monda
daa0e723b1 Display more detailed "invalid hardware configuration" errors. 2018-05-21 11:03:05 +02:00
Róbert Kiss
609aba856a fix: permission detection on win and mac (#650) 2018-05-21 11:00:57 +02:00
Róbert Kiss
a6678bd537 feat: update firmware version after update (#649)
* feat: add clipboard copy icon to the x-term-component

* feat: start device poll after firmware upgrade

* feat: remove the OK button from the firmware upgrade page

* feat: read the firmware after firmware upgrade

* fix: scrolling of the x-term-component

* feat: refresh the firmware version after recovery device

* fix: remove the scrollbar styling

* fix: stay on device firmware upgrade screen
2018-05-21 10:57:34 +02:00
László Monda
6c4f580fc2 Check if the keyboard is in factory reset mode and if so, display a relevant instruction. 2018-05-20 01:40:45 +02:00
László Monda
ea41661c65 Add erase-user-config.js 2018-05-19 21:53:41 +02:00
László Monda
c553c7b63b Rename erase-hca.js to erase-hardware-config.js 2018-05-19 21:33:12 +02:00
László Monda
e5988aa800 Rename write-hca.js to write-hardware-config.js 2018-05-19 21:32:15 +02:00
László Monda
ae319c607f Rename write-userconfig.js to write-user-config.js 2018-05-19 21:14:48 +02:00
László Monda
5d23ad1c9e Fix write-hca.js and write-user.js, and remove write-config.js. Fixes #627. 2018-05-19 20:59:11 +02:00
László Monda
55eef50da7 Add erase-hca.js 2018-05-19 20:05:41 +02:00
Róbert Kiss
653465f0e0 feat: device recovery mode (#642)
* add new page and ipc processing

* refactor: remove unused references from uhk.js

* feat: add device recovery route

* refactor: device permission

* feat: write firmware update log to the screen

* fix: xterm height

* feat: add reload button to the recovery page

* refactor: deviceConnectionState.hasPermission in appStartInfo

* refactor: use correct imports

* refactor: move .ok-button css class to the main style.scss

* feat: add bootload active route guard

* style: move RecoveryDeviceAction into new line

* feat: delete reload button

* feat: start device polling after device recovery
2018-05-19 17:22:46 +02:00
László Monda
2cf8044987 Check the signature of the hardware configuration area instead of uniqueId. 2018-05-19 14:07:18 +02:00
László Monda
3c056a7255 Display "Invalid hardware configuration" when the hardware configuration area is uninitialized instead of a general error message. Improves #623. 2018-05-19 13:38:16 +02:00
Róbert Kiss
091796d13c feat: not allow non ascii character in macro action text (#645) 2018-05-17 23:56:36 +02:00
László Monda
eb97dd844f Make the bootloader timeout of the reenumerate script specifiable. 2018-05-16 23:19:36 +02:00
Róbert Kiss
17693ec8fe build: upgrade node.js => 8.11.2 (#641) 2018-05-16 22:08:11 +02:00
Róbert Kiss
7c7ce8f50f feat: only send auto update notification when user indicated the update (#640)
* feat: only send auto update notification when user indicated the update

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

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

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

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

* fix electron renderer build

* fix webpack config

* format webpack.config

* fix renderer build
2018-04-10 19:52:58 +02:00
László Monda
1d4bb6113c Bump version to 1.1.5 in package.json and update the changelog. 2018-04-10 11:42:34 +02:00
Róbert Kiss
136120b831 feat: not allow run multiple Agent (#603) 2018-04-09 21:09:09 +02:00
Róbert Kiss
cd299c06d6 feat: not allow run multiple Agent (#604) 2018-04-09 20:48:22 +02:00
430 changed files with 32952 additions and 28081 deletions

2
.nvmrc
View File

@@ -1 +1 @@
8.9.4
12.0.0

View File

@@ -6,6 +6,184 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
## [1.2.13] - 2019-07-14
Firmware: 8.5.**4** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.5.4)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Implement the Kinetis bootloader protocol natively instead of relying on blhost.
- Fix device recovery mode.
- Correctly display whether the UHK is detected.
- Animate keyboard splitting, merging, and the presence of the left half.
- Don't disable input in the key action popover after adding a layer switch action, deleting it, and trying to edit it on its layer.
- Provide reasonable default mouse settings for Macs.
- Don't change tab immediately upon closing the key action popover.
- Fix UI glitch that occurrs when hitting Tab after updating keymap description.
- Make the Agent icon slightly smaller to be consistent with most application icons.
- Redesign the About page.
- Link the UHK knowledgebase from the help page.
- Make the middle mouse button not open new windows on links in Agent.
- Add top auto-update notification bar.
- Save window state when closing Agent.
- Hide USB usage data in console.
## [1.2.12] - 2018-11-14
Firmware: 8.**5.3** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.5.3)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- When the firmware of the right keyboard half is larger or equal than 8.4.3 then display the "Lock layer when double tapping this key" checkbox and remove "... macro playback is not implemented yet..." notices.
- Upgrade to node-hid 0.7.3 which utilizes the hidraw USB driver on Linux instead of libusb.
- Update udev rules for the new hidraw based node-hid.
- Improve the "Cannot find your UHK" and the privilege escalation screens to show more relevant messages when transitioning from the libusb based node-hid to the hidraw based node-hid.
- Fix the rendering of macro actions, so that their text doesn't overlap.
- Add "International {1,2,3}" and "Language {1,2}" keypress actions.
- Add icon for the Play/Pause keypress action.
- Remove the Stop/Eject keypress action.
- Make the "Type text" macro action accept clipboard data on Mac.
- Display "You can't change this mapping because on the base layer a layer switcher key targets this key." in the key action popover whenever it applies.
- Fix UI bug which could be triggered by tapping Tab in the keymap abbreviation input.
- Don't trigger Agent shortcuts when capturing keypresses.
- Log USB device list before checking permissions.
- Show OS-specific modifiers in the title bar of macro actions.
- Only show the device list on Linux when the list actually changes.
## [1.2.11] - 2018-10-03
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Add backspace and caps lock icons which avoids the overlap of their old texts.
- Fix right and middle mouse click macro actions which were exchanged.
- Include Agent version to the firmware update log.
## [1.2.10] - 2018-09-24
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Add History Back and History Forward scancodes.
- Save the actual decelerated scroll speed instead of using the accelerated scroll speed by accident.
- Allow layer switcher secondary roles only on the base layer.
- When remapping modifiers, display a warning suggesting to remap them on all layers.
- Display more exact instructions on the permission setup screen.
- Set the decelerated scroll speed of the default configuration from 20 to 10.
- Map Caps Lock without Ctrl on default keymaps.
- Rename "Scroll Lock" to "ScrLk" and "Num Lock" to "NumLk" on keys to avoid text overlap.
- In the scancode select2, display "Print Screen SysRq" and add SysRq above PrtScn when rendering the key.
- Fix left and right direction titles for mouse movement macro actions.
## [1.2.9] - 2018-09-13
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Display OS-specific modifiers.
- Display secondary roles.
- Don't trigger "Remap on all layers" after leaving Agent with Alt+Tab.
## [1.2.8] - 2018-08-26
Firmware: 8.**2.5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Uncheck the "Remap on all keymaps" and "Remap on all layers" checkboxes of the key action popover by default.
- Bind left and right Shift on the Mouse layer of all keymaps in the default configuration.
- Make ng2-select2 widgets faster.
- Add note to the LED brightness page saying that current UHK versions are not backlit.
- Fix the padding of the secondary role tooltip.
- Remove the redundant scrollbar from the LED brightness page.
## [1.2.7] - 2018-07-26
Firmware: 8.4.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Fix Agent startup exception on Linux by upgrading Electron builder.
- Change the shortcut which enables the USB stack test code, so that it can be triggered with the default Mac US keymap.
## [1.2.6] - 2018-07-26
Firmware: 8.**4.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.**4.0** | User Config: 4.0.1 | Hardware Config: 1.0.0
- Replace the Linux blhost binary with a statically compiled version that doesn't use special instructions and shouldn't segfault.
- Keep the current layer when changing keymaps.
- Fix the sleep key of Mac keymaps.
- Add help page.
- Add "save to keyboard" and "remap key" shortcuts.
- Build only AppImages for Linux.
- Replace ng2-select2 widgets with ngx-select-ex that always shows up in the correct position.
- Improve the phrasing of the firmware update error message.
- Tweak unsupported Windows firmware update notification.
- Hide the Settings menu until auto update is implemented.
- Don't scroll when the macro tab of the key action popover gets selected.
- Add keyboard shortcut for enabling the USB stack test mode of the firmware. `DEVICEPROTOCOL:MINOR`
- Tone down the color of the separator line.
## [1.2.5] - 2018-06-26
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
- When remapping a switch keymap action on all keymaps, don't set it on its own keymap.
- Make the key action popover always contain the action of the current key, even after cancelled.
- Include the firmware version to be updated to the firmware update log.
- Update the Agent icon of the side menu and the about page.
- When remapping a key, only flash the affected key instead of all keys.
- Fade in/out the keyboard separator line only when splitting the keyboard.
- Only show the unsupported OS message of the firmware page on relevant Windows versions.
- Close and reopen USB device when an error occurs.
- Temporarily remove the export keymap feature because it's useless until import is implemented.
## [1.2.4] - 2018-06-21
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Replace Linux x86-64 blhost with a statically linked version which should make firmware updates work on every Linux distro.
## [1.2.3] - 2018-06-19
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Add checkboxes for remapping keys on all layers and/or all keymaps.
- Add separator line between the keyboard halves.
- Add double tap icon for switch layer actions.
- Improve the looks and content of the tooltips of the key action popover.
- Make the left keyboard half less likely to timeout during firmware update.
- Terminate the firmware update process if blhost segfaults.
- Replace the Linux x86-64 version of the blhost binary which should not make it segfault anymore.
- Make the firmware update log shorter by listing one device per line and not repeating the list of available USB devices.
- Make the firmware update help text shorter.
## [1.2.2] - 2018-05-27
Firmware: 8.2.**5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.**1** | User Config: 4.0.1 | Hardware Config: 1.0.0
- Offer recovery for bricked right keyboard halfs.
- Detect when the hardware configuration of a device is invalid and display a notification. `DEVICEPROTOCOL:PATCH`
- Check if the keyboard is in factory reset mode and if so, display a relevant instruction.
- Only allow ASCII characters in type text macro actions.
- Allow uploading the same file multiple times in a row.
- Only send auto update notification when the user initiates the update.
- Update the firmware versions on the firmware update page right after firmware updates.
- Add a lot of useful instructions to the firmware page to help users update the firmware.
- Add the operating system and initial device list to the firmware update log.
- Add copy to clipboard button to the top right corner of the firmware update terminal widget.
## [1.2.1] - 2018-05-12
Firmware: 8.2.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.2)] | Device Protocol: 4.3.0 | User Config: 4.0.**1** | Hardware Config: 1.0.0
- Match for the new USB usage page and usage number. This is critical for UHKs flashed with firmware >=8.2.2 to be recognized by Agent on OSX.
- Make the config serializer handle long media macro actions. `USERCONFIG:PATCH`
- Add note on the macro page explaining that the macro engine of the firmware is not ready yet.
- Add an example to the scancode tooltip to better explain users how to invoke non-US characters.
## [1.2.0] - 2018-04-20
Firmware: 8.**2.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.0)] | Device Protocol: 4.**3.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
- Tweak the default mouse speed. This was necessary because the last firmware version adjusted speed multipliers. The mouse speed can be reset via the "Reset speeds to default" button of the "Mouse speed" page.
- Make the newly added switch-keymap.js script utilize the new UsbCommandId_SwitchKeymap, allowing for programmatic keymap switching. `DEVICEPROTOCOL:MINOR`
## [1.1.5] - 2018-04-10
Firmware: 8.1.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Don't allow to run multiple instances of Agent at the same time, but rather focus the already existing Agent window.
## [1.1.4] - 2018-04-09
Firmware: 8.1.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
@@ -50,7 +228,7 @@ Firmware: 8.1.**2** [[release](https://github.com/UltimateHackingKeyboard/firmwa
## [1.1.0] - 2018-01-15
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.**2.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
- Only accept device, keymap, and macro names upon editing if their trimmed length is non-zero.
- Add diagnostics USB scripts, most notably /packages/usb/{get-i2c-health,set-i2c-baud-rate}.js, some utilizing new device protocol commands and properties. `DEVICEPROTOCOL:MINOR`

View File

@@ -1,3 +1,17 @@
# Signing the CLA
Before contributing to this project, you must sign [the CLA](/cla/cla-1.0.0.md).
To sign the CLA, add your GitHub username to the end of the CLA. Make sure that the usernames remain alphabetically sorted.
Then create a pull request with the title:
> Sign CLA
and with the body:
> I have read the Agreement, and fully agree to it by signing it with my GitHub username.
# Bug reports
If the build process fails, please open a [new issue](https://github.com/UltimateHackingKeyboard/agent/issues/new) containing the complete build log.

10
ISSUE_TEMPLATE Normal file
View File

@@ -0,0 +1,10 @@
Before submitting a new issue, make sure to do the following:
1. If you're using Karabiner Elements on your Mac, close it!
2. Install the latest Agent:
https://github.com/UltimateHackingKeyboard/agent/releases/latest
3. Use Agent to update to the latest firmware:
https://github.com/UltimateHackingKeyboard/firmware/releases/latest
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

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

View File

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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 913 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 20 KiB

112
cla/cla-1.0.0.md Normal file
View File

@@ -0,0 +1,112 @@
# Contributor Agreement
## Individual Contributor Exclusive License Agreement version 1.0.0
Thank you for your interest in contributing to Ultimate Gadget Laboratories Kft.'s Ultimate Hacking Keyboard Agent ("We" or "Us").
The purpose of this contributor agreement ("Agreement") is to clarify and document the rights granted by contributors to Us.
By signing this Agreement, you agree that the following terms apply to all of your past, present and future contributions to the project.
## How to use this Contributor Agreement
If You are an employee and have created the Contribution as part of your employment, You need to have Your employer approve this Agreement or sign the Entity version of this document. If You do not own the Copyright in the entire work of authorship, any other author of the Contribution should also sign this in any event, please contact Us at support@UltimateHackingKeyboard.com
## 1. Definitions
**"You"** means the individual Copyright owner who Submits a Contribution to Us.
**"Contribution"** means any original work of authorship, including any original modifications or additions to an existing work of authorship, Submitted by You to Us, in which You own the Copyright.
**"Copyright"** means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence.
**"Material"** means the software or documentation made available by Us to third parties. When this Agreement covers more than one software project, the Material means the software or documentation to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material.
**"Submit"** means any act by which a Contribution is transferred to Us by You by means of tangible or intangible media, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us, but excluding any transfer that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
**"Documentation"** means any non-software portion of a Contribution.
## 2. License grant
### 2.1 Copyright license to Us
Subject to the terms and conditions of this Agreement, You hereby grant to Us a worldwide, royalty-free, exclusive, perpetual and irrevocable (except as stated in Section 8.2) license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to:
* publish the Contribution,
* modify the Contribution,
* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials,
* reproduce the Contribution in original or modified form,
* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form.
### 2.2 Moral rights
Moral Rights remain unaffected to the extent they are recognized and not waivable by applicable law.
### 2.3 Copyright license back to You
Upon such grant of rights to Us, We immediately grant to You a worldwide, royalty-free, non-exclusive, perpetual and irrevocable license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contribution to use the Contribution by all means, including, but not limited to:
* publish the Contribution,
* modify the Contribution,
* prepare derivative works based upon or containing the Contribution and/or to combine the Contribution with other Materials,
* reproduce the Contribution in original or modified form,
* distribute, to make the Contribution available to the public, display and publicly perform the Contribution in original or modified form.
This license back is limited to the Contribution and does not provide any rights to the Material.
## 3. Patents
### 3.1 Patent license
Subject to the terms and conditions of this Agreement You hereby grant to Us and to recipients of Materials distributed by Us a worldwide, royalty-free, non-exclusive, perpetual and irrevocable (except as stated in Section 3.2) patent license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with any Material (and portions of such combination). This license applies to all patents owned or controlled by You, whether already acquired or hereafter acquired, that would be infringed by making, having made, using, selling, offering for sale, importing or otherwise transferring of Your Contribution(s) alone or by combination of Your Contribution(s) with any Material.
### 3.2 Revocation of patent license
You reserve the right to revoke the patent license stated in section 3.1 if We make any infringement claim that is targeted at your Contribution and not asserted for a Defensive Purpose. An assertion of claims of the Patents shall be considered for a "Defensive Purpose" if the claims are asserted against an entity that has filed, maintained, threatened, or voluntarily participated in a patent infringement lawsuit against Us or any of Our licensees.
## 4. Disclaimer
THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US AND BY US TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION AND EXTENT TO THE MINIMUM PERIOD AND EXTENT PERMITTED BY LAW.
## 5. Consequential damage waiver
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
## 6. Approximation of disclaimer and damage waiver
IF THE DISCLAIMER AND DAMAGE WAIVER MENTIONED IN SECTION 4. AND SECTION 5. CANNOT BE GIVEN LEGAL EFFECT UNDER APPLICABLE LOCAL LAW, REVIEWING COURTS SHALL APPLY LOCAL LAW THAT MOST CLOSELY APPROXIMATES AN ABSOLUTE WAIVER OF ALL CIVIL OR CONTRACTUAL LIABILITY IN CONNECTION WITH THE CONTRIBUTION.
## 7. Term
7.1 This Agreement shall come into effect upon Your acceptance of the terms and conditions.
7.2 In the event of a termination of this Agreement Sections 4, 5, 6, 7 and 8 shall survive such termination and shall remain in full force thereafter. For the avoidance of doubt, (sub)licenses that have already been granted for Contributions at the date of the termination shall remain in full force after the termination of this Agreement.
## 8. Miscellaneous
8.1 This Agreement and all disputes, claims, actions, suits or other proceedings arising out of this agreement or relating in any way to it shall be governed by the laws of Hungary excluding its private international law provisions.
8.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings.
8.3 In case of Your death, this agreement shall continue with Your heirs. In case of more than one heir, all heirs must exercise their rights through a commonly authorized person.
8.4 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and that is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law.
8.5 You agree to notify Us of any facts or circumstances of which you become aware that would make this Agreement inaccurate in any respect.
## 9. Signatures
I have read this Agreement, and fully agree to it by signing it with my GitHub username:
- @attilacsanyi
- @cokert
- @csanyiarpad
- @dgyimesi
- @eltang
- @ert78gb
- @fjozsef
- @laxu
- @mondalaci
- @NejcZdovc
- @spuder
- @srang
- @Lauszus

18815
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,10 +3,10 @@
"private": true,
"author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js",
"version": "1.1.4",
"firmwareVersion": "8.1.5",
"deviceProtocolVersion": "4.2.0",
"userConfigVersion": "4.0.0",
"version": "1.2.13",
"firmwareVersion": "8.6.0",
"deviceProtocolVersion": "4.5.0",
"userConfigVersion": "4.1.1",
"hardwareConfigVersion": "1.0.0",
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
"repository": {
@@ -15,55 +15,66 @@
},
"license": "GPL-3.0",
"engines": {
"node": ">=8.9.1 <9.0.0",
"npm": ">=5.6.0 <6.0.0"
"node": ">=12.0.0 <13.0.0",
"npm": ">=6.9.0 <7.0.0"
},
"devDependencies": {
"@types/decompress": "4.2.3",
"@types/electron-devtools-installer": "2.0.2",
"@types/electron-settings": "3.0.0",
"@types/fs-extra": "5.0.1",
"@types/jasmine": "2.6.0",
"@types/jsonfile": "4.0.1",
"@types/file-saver": "0.0.1",
"@types/fs-extra": "8.0.0",
"@types/jasmine": "3.3.12",
"@types/jasminewd2": "2.0.3",
"@types/jquery": "3.3.29",
"@types/jsonfile": "5.0.0",
"@types/lodash": "4.14.136",
"@types/node": "8.0.53",
"@types/node-hid": "0.5.2",
"@types/node-hid": "0.7.2",
"@types/request": "2.0.8",
"@types/usb": "1.1.3",
"autoprefixer": "6.5.3",
"@types/semver": "5.5.0",
"@types/tmp": "0.0.33",
"buffer": "5.0.6",
"copyfiles": "^2.0.0",
"copy-webpack-plugin": "4.0.1",
"check-node-version": "4.0.1",
"copy-webpack-plugin": "5.0.0",
"copyfiles": "2.1.1",
"core-js": "2.4.1",
"cross-env": "5.0.5",
"decompress": "4.2.0",
"decompress-tarbz2": "^4.1.1",
"decompress-tarbz2": "4.1.1",
"devtron": "1.4.0",
"electron": "1.8.4",
"electron-builder": "20.8.1",
"electron": "5.0.9",
"electron-builder": "20.44.4",
"electron-debug": "1.5.0",
"electron-devtools-installer": "2.2.3",
"electron-log": "2.2.14",
"electron-rebuild": "1.7.3",
"electron-log": "2.2.16",
"electron-rebuild": "1.8.6",
"electron-settings": "3.1.4",
"electron-updater": "2.21.4",
"exports-loader": "0.6.3",
"file-loader": "0.10.0",
"fs-extra": "5.0.0",
"jsonfile": "4.0.0",
"lerna": "2.9.0",
"mkdirp": "0.5.1",
"node-hid": "0.5.7",
"npm-run-all": "4.0.2",
"electron-updater": "4.1.2",
"fs-extra": "8.1.0",
"gh-pages": "2.0.1",
"jasmine": "3.4.0",
"jasmine-core": "3.4.0",
"jasmine-node": "3.0.0",
"jasmine-ts": "0.3.0",
"jsonfile": "5.0.0",
"lerna": "3.16.4",
"lodash": "4.17.15",
"node-hid": "0.7.9",
"npm-run-all": "4.1.5",
"nrf-intel-hex": "1.3.0",
"pre-commit": "1.2.2",
"request": "2.83.0",
"request": "2.88.0",
"rimraf": "2.6.1",
"standard-version": "4.2.0",
"stylelint": "7.13.0",
"svg-sprite": "1.3.7",
"ts-loader": "2.3.1",
"ts-node": "3.0.4",
"tslint": "5.9.1",
"typescript": "2.5.2",
"webpack": "2.4.1"
"source-map-support": "0.5.9",
"stylelint": "10.0.0",
"svg-sprite": "1.5.0",
"ts-loader": "5.3.3",
"ts-node": "8.3.0",
"tslint": "5.18.0",
"typescript": "3.5.3",
"webpack": "4.29.5",
"webpack-cli": "3.2.3"
},
"pre-commit": [
"precommit-msg"
@@ -71,34 +82,22 @@
"scripts": {
"postinstall": "lerna bootstrap",
"precommit-msg": "Git precommit hook is running... & exit 0",
"test": "run-p -sn test:test-serializer test:uhk-common",
"test:test-serializer": "lerna exec --scope test-serializer npm test",
"test:uhk-common": "lerna exec --scope uhk-common npm test",
"test:uhk-web": "lerna exec --scope uhk-web npm test",
"lint": "run-s -scn lint:ts lint:style",
"lint:ts": "run-p -sn lint:ts:electron-main lint:ts:electron-renderer lint:ts:web lint:ts:test-serializer lint:ts:uhk-usb",
"lint:ts:electron-main": "tslint --project ./packages/uhk-agent/tsconfig.json",
"lint:ts:electron-renderer": "tslint --project ./packages/uhk-web/src/tsconfig.renderer.json",
"lint:ts:web": "tslint --project ./packages/uhk-web/src/tsconfig.app.json",
"lint:ts:test-serializer": "tslint --project ./packages/test-serializer/tsconfig.json",
"lint:ts:uhk-usb": "tslint --project ./packages/uhk-usb/tsconfig.json",
"lint:style": "stylelint \"packages/uhk-agent/src/**/*.scss\" \"packages/uhk-web/src/**/*.scss\" --syntax scss",
"build": "run-s build:common build:usb build:web build:electron",
"build:web": "lerna exec --scope uhk-web npm run build",
"build:electron": "cross-env AOT_BUILD=true run-s -sn build:electron:renderer build:electron:main",
"build:electron:main": "lerna exec --scope uhk-agent npm run build",
"build:electron:renderer": "lerna exec --scope uhk-web npm run build:renderer",
"build:common": "lerna exec --scope uhk-common npm run build",
"build:usb": "lerna exec --scope uhk-usb npm run build",
"test": "lerna run test",
"lint": "lerna run lint",
"e2e": "lerna run e2e --scope uhk-web",
"prebuild": "check-node-version --package",
"build": "lerna run build",
"server:web": "lerna exec --scope uhk-web npm start",
"server:electron": "lerna exec --scope uhk-web npm run server:renderer",
"electron": "lerna exec --scope uhk-agent npm start",
"electron:spe": "lerna exec --scope uhk-agent npm run electron:spe",
"standard-version": "standard-version",
"electron:kboot": "lerna exec --scope uhk-agent npm run electron:kboot",
"pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites",
"release": "node ./scripts/release.js",
"clean": "lerna exec rimraf ./node_modules ./dist"
},
"dependencies": {}
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist ./tmp",
"predeploy-gh-pages": "lerna run build:web --scope=uhk-web",
"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"
}
}

1
packages/kboot/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './src';

View File

@@ -0,0 +1,8 @@
{
"spec_dir": "test",
"spec_files": [
"**/*[sS]pec.ts"
],
"stopSpecOnExpectationFailure": true,
"random": false
}

532
packages/kboot/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"name": "kboot",
"main": "dist/index.js",
"version": "0.0.0",
"description": "Javascript implementation of the Kinetis Bootloader protocol",
"author": "Ultimate Gadget Laboratories",
"repository": {
"type": "git",
"url": "git@github.com:UltimateHackingKeyboard/agent.git"
},
"license": "GPL-3.0",
"dependencies": {
"debug": "^4.1.1",
"byte-data": "^16.0.3",
"tslib": "^1.10.0",
"node-hid": ">= 0.7.9"
},
"peer-dependencies": {},
"scripts": {
"build": "tsc --project ./src/tsconfig.json",
"lint": "tslint --project tsconfig.json"
}
}

65
packages/kboot/readme.md Normal file
View File

@@ -0,0 +1,65 @@
Javascript implementation of the Kinetis Bootloader protocol
============================================================
Based on the [Kinetis Bootloader v2.0.0 Reference Manual](https://github.com/UltimateHackingKeyboard/bootloader/blob/master/doc/Kinetis%20Bootloader%20v2.0.0%20Reference%20Manual.pdf)
## Supported communication channels/protocols
- [x] USB
- [ ] I2C
- [ ] SPI
- [ ] CAN
- [ ] UART
## Supported Commands
We implemented only the commands that is used in UHK software.
If someone needs other commands, (s)he can easily implement it based on existing.
- [x] GetProperty
- [ ] SetProperty
- [ ] FlashEraseAll
- [x] FlashEraseRegion
- [x] FlashEraseAllUnsecure
- [x] ReadMemory
- [x] WriteMemory
- [ ] FillMemory
- [x] FlashSecurityDisable
- [ ] Execute
- [ ] Call
- [x] Reset
- [ ] FlashProgramOnce
- [ ] FlashReadOnce
- [ ] FlashReadResource
- [ ] ConfigureQuadSpi
- [ ] ReliableUpdate
- [x] ConfigureI2c
- [ ] ConfigureSpi
- [ ] ConfigureCan
## How to use
```Typescript
// Initialize peripheral
const usbPeripheral = new UsbPeripheral({ productId: 1, vendorId: 1 });
// Initialize Kboot
const kboot = new KBoot(usbPeripheral);
// Call the command
const version = await kboot.getBootloaderVersion();
// ... more commands
// Close the communication channel. Release resources
kboot.close();
```
If you have to communicate other I2C device over USB call `kboot.configureI2c(i2cId)` before the command.
```Typescript
const usbPeripheral = new UsbPeripheral({ productId: 1, vendorId: 1 });
const kboot = new KBoot(usbPeripheral);
// Get the bootloader version of I2C device
await kboot.configureI2c(i2cId);
const version = await kboot.getBootloaderVersion();
```
## TODO
- [ ] Improve exception handling

View File

@@ -0,0 +1,2 @@
export const DEFAULT_USB_PID = 0x0073;
export const DEFAULT_USB_VID = 0x15A2;

View File

@@ -0,0 +1,23 @@
export enum Commands {
FlashEraseAll = 0x01,
FlashEraseRegion = 0x02,
ReadMemory = 0x03,
WriteMemory = 0x04,
FillMemory = 0x05,
FlashSecurityDisable = 0x06,
GetProperty = 0x07,
ReceiveSBFile = 0x08,
Execute = 0x09,
Call = 0x0A,
Reset = 0x0B,
SetProperty = 0x0C,
FlashEraseAllUnsecure = 0x0D,
FlashProgramOnce = 0x0E,
FlashReadOnce = 0x0F,
FlashReadResource = 0x10,
ConfigureQuadSpi = 0x11,
ReliableUpdate = 0x12,
ConfigureI2c = 0xc1,
ConfigureSpi = 0xc2,
ConfigureCan = 0xc3
}

View File

@@ -0,0 +1,5 @@
export * from './commands';
export * from './memory-ids';
export * from './properties';
export * from './response-codes';
export * from './response-tags';

View File

@@ -0,0 +1,5 @@
export enum MemoryIds {
Internal = 0,
Spi0 = 1,
ExecuteOnly = 0x10
}

View File

@@ -0,0 +1,26 @@
export enum Properties {
BootloaderVersion = 0x01,
AvailablePeripherals = 0x02,
FlashStartAddress = 0x03,
FlashSize = 0x04,
FlashSectorSize = 0x05,
FlashBlockCount = 0x06,
AvailableCommands = 0x07,
CrcCheckStatus = 0x08,
VerifyWrites = 0x0A,
MaxPacketSize = 0x0B,
ReservedRegions = 0x0C,
ValidateRegions = 0x0D,
RAMStartAddress = 0x0E,
RAMSize = 0x0F,
SystemDeviceIdent = 0x10,
FlashSecurityState = 0x11,
UniqueDeviceIdent = 0x12,
FlashFacSupport = 0x13,
FlashAccessSegmentSize = 0x14,
FlashAccessSegmentCount = 0x15,
FlashReadMargin = 0x16,
QspiInitStatus = 0x17,
TargetVersion = 0x18,
ExternalMemoryAttributes = 0x19
}

View File

@@ -0,0 +1,76 @@
export enum ResponseCodes {
// Generic status codes.
Success = 0,
Fail = 1,
ReadOnly = 2,
OutOfRange = 3,
InvalidArgument = 4,
// Flash driver errors.
FlashSizeError = 100,
FlashAlignmentError = 101,
FlashAddressError = 102,
FlashAccessError = 103,
FlashProtectionViolation = 104,
FlashCommandFailure = 105,
FlashUnknownProperty = 106,
// I2C driver errors.
I2C_SlaveTxUnderrun = 200,
I2C_SlaveRxOverrun = 201,
I2C_AribtrationLost = 202,
// SPI driver errors.
SPI_SlaveTxUnderrun = 300,
SPI_SlaveRxOverrun = 301,
// QuadSPI driver errors
QSPI_FlashSizeError = 400,
QSPI_FlashAlignmentError = 401,
QSPI_FlashAddressError = 402,
QSPI_FlashCommandFailure = 403,
QSPI_FlashUnknownProperty = 404,
QSPI_NotConfigured = 405,
QSPI_CommandNotSupported = 406,
// Bootloader errors.
UnknownCommand = 10000,
SecurityViolation = 10001,
AbortDataPhase = 10002,
PingError = 10003,
NoResponse = 10004,
NoResponseExpected = 10005,
// SB loader errors.
RomLdrSectionOverrun = 10100,
RomLdrSignature = 10101,
RomLdrSectionLength = 10102,
RomLdrUnencryptedOnly = 10103,
RomLdrEOFReached = 10104,
RomLdrChecksum = 10105,
RomLdrCrc32Error = 10106,
RomLdrUnknownCommand = 10107,
RomLdrIdNotFound = 10108,
RomLdrDataUnderrun = 10109,
RomLdrJumpReturned = 10110,
RomLdrCallFailed = 10111,
RomLdrKeyNotFound = 10112,
RomLdrSecureOnly = 10113,
// Memory interface errors.
MemoryRangeInvalid = 10200,
MemoryReadFailed = 10201,
MemoryWriteFailed = 10202,
// Property store errors.
UnknownProperty = 10300,
ReadOnlyProperty = 10301,
InvalidPropertyValue = 10302,
// Property store errors.
AppCrcCheckPassed = 10400,
AppCrcCheckFailed = 10401,
AppCrcCheckInactive = 10402,
AppCrcCheckInvalid = 10403,
AppCrcCheckOutOfRange = 10404
}

View File

@@ -0,0 +1,7 @@
export enum ResponseTags {
Generic = 0xA0,
ReadMemory = 0xA3,
Property = 0xA7,
FlashReadOnce = 0xAF,
FlashReadResource = 0xB0
}

View File

@@ -0,0 +1,6 @@
export * from './kboot';
export * from './enums';
export * from './models';
export * from './peripheral';
export * from './usb-peripheral';
export * from './util';

221
packages/kboot/src/kboot.ts Normal file
View File

@@ -0,0 +1,221 @@
import { debug } from 'debug';
import { pack } from 'byte-data';
import { Peripheral } from './peripheral';
import { Commands, MemoryIds, Properties, ResponseCodes, ResponseTags } from './enums';
import { BootloaderVersion, CommandOption, CommandResponse, DataOption } from './models';
const logger = debug('kboot');
export class KBoot {
constructor(private peripheral: Peripheral) {
}
open(): void {
logger('Open peripheral');
this.peripheral.open();
}
close(): void {
logger('Close peripheral');
this.peripheral.close();
}
// ================= Read properties ==================
async getProperty(property: Properties, memoryId = MemoryIds.Internal): Promise<CommandResponse> {
logger('Start read memory %o', { property, memoryId });
const command: CommandOption = {
command: Commands.GetProperty,
params: [
...pack(property, { bits: 32 }),
...pack(memoryId, { bits: 32 })
]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Property) {
logger('Response tag is not property response: %d', property);
throw new Error('Response tag is not property response');
}
if (response.code === ResponseCodes.UnknownProperty) {
logger('Unknown property %d', response.code);
throw new Error('Unknown property!');
}
if (response.code !== ResponseCodes.Success) {
logger('Unknown error %d', response.code);
throw new Error(`Unknown error. Error code:${response.code}`);
}
return response;
}
async getBootloaderVersion(): Promise<BootloaderVersion> {
logger('Start to read Bootloader Version');
const response = await this.getProperty(Properties.BootloaderVersion);
const version: BootloaderVersion = {
bugfix: response.raw[12],
minor: response.raw[13],
major: response.raw[14],
protocolName: String.fromCharCode(response.raw[15])
};
logger('bootloader version %o');
return version;
}
// TODO: Implement other get/set property wrappers
// ================= End read properties ==================
async flashSecurityDisable(key: number[]): Promise<void> {
logger('Start flash security disable %o', { key });
if (key.length !== 8) {
logger('Error: Flash security key must be 8 byte. %o', key);
throw new Error('Flash security key must be 8 byte');
}
const command: CommandOption = {
command: Commands.FlashSecurityDisable,
params: [...key]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Can not disable flash security: %d', response.code);
throw new Error(`Can not disable flash security`);
}
}
async flashEraseRegion(startAddress: number, count: number): Promise<void> {
logger('Start flash erase region');
const command: CommandOption = {
command: Commands.FlashEraseRegion,
params: [
...pack(startAddress, { bits: 32 }),
...pack(count, { bits: 32 })
]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Can not flash erase region: %d', response.code);
throw new Error(`Can not flash erase region`);
}
}
async flashEraseAllUnsecure(): Promise<void> {
logger('Start flash erase all unsecure');
const command: CommandOption = {
command: Commands.FlashEraseAllUnsecure,
params: []
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Can not flash erase all unsecure: %d', response.code);
throw new Error(`Can not flash erase all unsecure`);
}
}
async readMemory(startAddress: number, count: number): Promise<any> {
logger('Start read memory %o', { startAddress, count });
return this.peripheral.readMemory(startAddress, count);
}
async writeMemory(options: DataOption): Promise<void> {
logger('Start write memory %o', { options });
return this.peripheral.writeMemory(options);
}
/**
* Reset the bootloader
*/
async reset(): Promise<void> {
logger('Start reset the bootloader');
const command: CommandOption = {
command: Commands.Reset,
params: []
};
let response: CommandResponse;
try {
response = await this.peripheral.sendCommand(command);
} catch (error) {
if (error.message === 'could not read from HID device') {
logger('Ignoring missing response from reset command.');
this.close();
return;
}
throw error;
}
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Unknown error %d', response.code);
throw new Error(`Unknown error. Error code:${response.code}`);
}
}
/**
* Call it before send data to I2C
* @param address - The address of the I2C
* @param [speed=64] - Speed of the I2C
*/
async configureI2c(address: number, speed = 64): Promise<void> {
logger('Start configure I2C', { address, speed });
if (address > 127) {
logger('Only 7-bit i2c address is supported');
throw new Error('Only 7-bit i2c address is supported');
}
const command: CommandOption = {
command: Commands.ConfigureI2c,
params: [
...pack(address, { bits: 32 }),
...pack(speed, { bits: 32 })
]
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
logger('Response tag is not generic response: %d', response.tag);
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
logger('Unknown error %d', response.code);
throw new Error(`Unknown error. Error code:${response.code}`);
}
}
}

View File

@@ -0,0 +1,6 @@
export interface BootloaderVersion {
major: number;
minor: number;
bugfix: number;
protocolName: string;
}

View File

@@ -0,0 +1,7 @@
import { Commands } from '../enums';
export interface CommandOption {
command: Commands;
hasDataPhase?: boolean;
params?: number[];
}

View File

@@ -0,0 +1,7 @@
import { ResponseCodes, ResponseTags } from '../enums';
export interface CommandResponse {
tag: ResponseTags;
code: ResponseCodes;
raw: Buffer;
}

View File

@@ -0,0 +1,4 @@
export interface DataOption {
startAddress: number;
data: Buffer;
}

View File

@@ -0,0 +1,5 @@
export * from './bootloader-version';
export * from './command-option';
export * from './command-response';
export * from './data-option';
export * from './usb';

View File

@@ -0,0 +1,7 @@
export interface USB {
vendorId: number;
productId: number;
interface?: number;
usage?: number;
usePage?: number;
}

View File

@@ -0,0 +1,13 @@
import { CommandOption, CommandResponse, DataOption } from './models';
export interface Peripheral {
open(): void;
close(): void;
sendCommand(options: CommandOption): Promise<CommandResponse>;
writeMemory(data: DataOption): Promise<void>;
readMemory(startAddress: number, count: number): Promise<Buffer>;
}

View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "../dist"
}
}

View File

@@ -0,0 +1,279 @@
import { debug } from 'debug';
import { devices, HID } from 'node-hid';
import { pack } from 'byte-data';
import { Peripheral } from './peripheral';
import { CommandOption, CommandResponse, DataOption, USB } from './models';
import { convertLittleEndianNumber, convertToHexString, deviceFinder, encodeCommandOption } from './util';
import { decodeCommandResponse } from './util/usb/decode-command-response';
import { validateCommandParams } from './util/usb/encode-command-option';
import { Commands, ResponseTags } from './enums';
import { snooze } from './util/snooze';
const logger = debug('kboot:usb');
const WRITE_DATA_STREAM_PACKAGE_LENGTH = 32;
export class UsbPeripheral implements Peripheral {
private _device: HID;
private _responseBuffer: Buffer;
private _dataBuffer: Buffer;
private _hidError: any;
constructor(private options: USB) {
logger('constructor options: %o', options);
}
open(): void {
this._hidError = undefined;
if (this._device) {
return;
}
logger('Available devices');
const device = devices()
.map(x => {
logger('%o', x);
return x;
})
.find(deviceFinder(this.options));
if (!device) {
logger('USB device can not be found %o', this.options);
throw new Error('USB device can not be found');
}
this._responseBuffer = Buffer.alloc(0);
this._dataBuffer = Buffer.alloc(0);
this._device = new HID(device.path);
this._device.on('data', this._usbDataListener.bind(this));
this._device.on('error', this._usbErrorListener.bind(this));
}
close(): void {
if (this._device) {
this._device.close();
this._device.removeAllListeners('data');
this._device.removeAllListeners('error');
this._device = undefined;
}
}
async sendCommand(options: CommandOption): Promise<CommandResponse> {
validateCommandParams(options.params);
const data = encodeCommandOption(options);
this._send(data);
return this._getNextCommandResponse();
}
writeMemory(option: DataOption): Promise<void> {
return new Promise<void>(async (resolve, reject) => {
try {
const command: CommandOption = {
command: Commands.WriteMemory,
hasDataPhase: true,
params: [
...pack(option.startAddress, { bits: 32 }),
...pack(option.data.length, { bits: 32 })
]
};
const firsCommandResponse = await this.sendCommand(command);
if (firsCommandResponse.tag !== ResponseTags.Generic) {
logger('Invalid write memory response! %o', firsCommandResponse);
return reject(new Error('Invalid write memory response!'));
}
if (firsCommandResponse.code !== 0) {
logger('Non zero write memory response! %o', firsCommandResponse);
return reject(new Error(`Non zero write memory response! Response code: ${firsCommandResponse.code}`));
}
for (let i = 0; i < option.data.length; i = i + WRITE_DATA_STREAM_PACKAGE_LENGTH) {
if (this._hidError) {
logger('Throw USB error %O', this._hidError);
return reject(new Error('USB error while write data'));
}
const slice = option.data.slice(i, i + WRITE_DATA_STREAM_PACKAGE_LENGTH);
const writeData = [
2, // USB channel
0,
slice.length,
0, // TODO: What is it?
...slice
];
logger('send data %o', convertToHexString(writeData));
this._device.write(writeData);
}
const secondCommandResponse = await this._getNextCommandResponse();
if (secondCommandResponse.tag !== ResponseTags.Generic) {
logger('Invalid write memory final response %o', secondCommandResponse);
return reject(new Error('Invalid write memory final response!'));
}
if (secondCommandResponse.code !== 0) {
logger('Non zero write memory final response %o', secondCommandResponse);
const msg = `Non zero write memory final response! Response code: ${secondCommandResponse.code}`;
return reject(new Error(msg));
}
resolve();
} catch (err) {
logger('Can not write memory data %O', err);
reject(err);
}
});
}
readMemory(startAddress: number, count: number): Promise<Buffer> {
return new Promise<Buffer>(async (resolve, reject) => {
try {
const command: CommandOption = {
command: Commands.ReadMemory,
params: [
...pack(startAddress, { bits: 32 }),
...pack(count, { bits: 32 })
]
};
this._resetDataBuffer();
this._resetResponseBuffer();
const firsCommandResponse = await this.sendCommand(command);
if (firsCommandResponse.tag !== ResponseTags.ReadMemory) {
logger('Invalid read memory response %o', firsCommandResponse);
return reject(new Error('Invalid read memory response!'));
}
if (firsCommandResponse.code !== 0) {
logger('Non zero read memory response %o', firsCommandResponse);
return reject(new Error(`Non zero read memory response! Response code: ${firsCommandResponse.code}`));
}
const byte4Number = firsCommandResponse.raw.slice(12, 15);
const arrivingData = convertLittleEndianNumber(byte4Number);
const memoryDataBuffer = await this._readFromDataStream(arrivingData);
const secondCommandResponse = await this._getNextCommandResponse();
if (secondCommandResponse.tag !== ResponseTags.Generic) {
logger('Invalid read memory final response %o', secondCommandResponse);
return reject(new Error('Invalid read memory final response!'));
}
if (secondCommandResponse.code !== 0) {
logger('Non zero read memory final response %o', secondCommandResponse);
const msg = `Non zero read memory final response! Response code: ${secondCommandResponse.code}`;
return reject(new Error(msg));
}
resolve(memoryDataBuffer);
} catch (error) {
logger('Read memory error %O', error);
reject(error);
}
});
}
private _send(data: number[]): void {
this.open();
logger('send data %o', `<${convertToHexString(data)}>`);
this._device.write(data);
}
private _usbDataListener(data: Buffer): void {
logger('received data %o', `[${convertToHexString(data)}]`);
const channel = data[0];
switch (channel) {
case 3:
this._responseBuffer = Buffer.concat([this._responseBuffer, data]);
break;
case 4:
this._dataBuffer = Buffer.concat([this._dataBuffer, data]);
break;
default:
logger('Unknown USB channel %o', channel);
break;
}
}
private _usbErrorListener(error: any): void {
logger('USB stream error %O', error);
this._hidError = error;
}
private _readFromCommandStream(byte = 36, timeout = 15000): Promise<Buffer> {
return this._readFromBuffer('_responseBuffer', byte, timeout);
}
private _readFromDataStream(byte = 36, timeout = 15000): Promise<Buffer> {
return this._readFromBuffer('_dataBuffer', byte, timeout);
}
private _readFromBuffer(bufferName: string, byte: number, timeout: number): Promise<Buffer> {
logger('start read from buffer %o', { bufferName, byte, timeout });
return new Promise<Buffer>(async (resolve, reject) => {
const startTime = new Date();
while (startTime.getTime() + timeout > new Date().getTime()) {
if (this._hidError) {
const err = this._hidError;
return reject(err);
}
const buffer: Buffer = this[bufferName];
if (buffer.length >= byte) {
const data = buffer.slice(0, byte);
if (buffer.length === byte) {
this[bufferName] = Buffer.alloc(0);
} else {
const newDataBuffer = Buffer.alloc(buffer.length - byte);
buffer.copy(newDataBuffer, 0, byte);
this[bufferName] = newDataBuffer;
}
logger(`read from ${bufferName}: %O`, convertToHexString(data));
return resolve(data);
}
await snooze(100);
}
logger('Timeout while try to read from buffer');
reject(new Error('Timeout while try to read from buffer'));
});
}
private _resetDataBuffer(): void {
this._dataBuffer = Buffer.alloc(0);
}
private _resetResponseBuffer(): void {
this._responseBuffer = Buffer.alloc(0);
}
private async _getNextCommandResponse(): Promise<CommandResponse> {
logger('Start read next command response');
const response = await this._readFromCommandStream();
const commandResponse = decodeCommandResponse(response);
logger('next command response: %o', commandResponse);
return commandResponse;
}
}

View File

@@ -0,0 +1,6 @@
export const encodeStringToParams = (data: string): Int8Array => {
// const buffer = Buffer.from(data);
// return new Int8Array(buffer, 0);
return new Int8Array(0);
};

View File

@@ -0,0 +1,22 @@
export * from './encode-string-to-parameters';
export * from './response-parser';
export * from './usb';
export const convertToHexString = (arr: number[] | Buffer): string => {
let str = '';
for (const n of arr) {
let hex = n.toString(16);
if (hex.length < 2) {
hex = '0' + hex;
}
if (str.length > 0) {
str += ' ';
}
str += hex;
}
return str;
};

View File

@@ -0,0 +1,21 @@
import { ResponseCodes, ResponseTags } from '../enums';
export const convertLittleEndianNumber = (data: Buffer): number => {
let value = 0;
for (let i = 0; i < data.length; i++) {
value += data[i] << (8 * i);
}
return value;
};
export const getResponseCode = (response: Buffer): ResponseCodes => {
const data = response.slice(8, 11);
return convertLittleEndianNumber(data);
};
export const getResponseTag = (response: Buffer): ResponseTags => {
return response[4];
};

View File

@@ -0,0 +1 @@
export const snooze = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

View File

@@ -0,0 +1,18 @@
import { CommandResponse } from '../../models';
import { getResponseCode, getResponseTag } from '../response-parser';
export const decodeCommandResponse = (response: Buffer): CommandResponse => {
if (response.length < 8) {
throw new Error('Invalid response length!');
}
if (response[0] !== 3) {
throw new Error(`Invalid response command channel!`);
}
return {
code: getResponseCode(response),
tag: getResponseTag(response),
raw: response
};
};

View File

@@ -0,0 +1,22 @@
import { isNullOrUndefined } from 'util';
import { Device } from 'node-hid';
import { USB } from '../../models';
export const deviceFinder = (usb: USB) => {
return (device: Device): boolean => {
if (device.productId !== usb.productId) {
return false;
}
if (device.vendorId !== usb.vendorId) {
return false;
}
// TODO: Add interface, usage and usePage filtering
// if (!isNullOrUndefined(usb.interface) && device.interface !== -1 && device.interface === usb.interface) {
// return true;
// }
return true;
};
};

View File

@@ -0,0 +1,46 @@
import { isNullOrUndefined } from 'util';
import { pack } from 'byte-data';
import { CommandOption } from '../../models';
/**
* Encode the USB Command.
* @param option
*/
export const encodeCommandOption = (option: CommandOption): number[] => {
const payload = [
option.command,
option.hasDataPhase ? 1 : 0,
0, // Reserved. Should be 0
option.params ? option.params.length / 4 >> 0 : 0 // number of parameters
];
if (option.params) {
payload.push(...option.params);
}
const header = [
1, // Communication channel
0, // TODO: What is it?
...pack(payload.length, { bits: 16 }) // payload length in 2 byte
];
const placeholders = new Array(32 - payload.length)
.fill(0);
return [...header, ...payload, ...placeholders];
};
export const validateCommandParams = (params: any[]): void => {
if (isNullOrUndefined(params)) {
return;
}
if (!Array.isArray(params)) {
throw new Error('Command parameters must be an array!');
}
if (params.length > 28) {
throw new Error('Maximum 7 (28 bytes) command parameters allowed!');
}
};

View File

@@ -0,0 +1,2 @@
export { deviceFinder } from './device-finder';
export { encodeCommandOption } from './encode-command-option';

View File

@@ -0,0 +1,36 @@
import { BootloaderVersion, CommandResponse, Commands, KBoot, Peripheral, Properties, ResponseCodes, ResponseTags } from '../src';
import { TestPeripheral } from './test-peripheral';
describe('kboot', () => {
let kboot: KBoot;
let testPeripheral: Peripheral;
beforeEach(() => {
testPeripheral = new TestPeripheral();
kboot = new KBoot(testPeripheral);
});
describe('getBootloaderVersion', () => {
it('should works', async () => {
const sendCommandResponse: CommandResponse = {
code: ResponseCodes.Success,
tag: ResponseTags.Property,
// tslint:disable-next-line:max-line-length
raw: Buffer.from([0x03, 0x00, 0x0c, 0x00, 0xa7, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
};
spyOn(testPeripheral, 'sendCommand').and.returnValue(Promise.resolve(sendCommandResponse));
const version = await kboot.getBootloaderVersion();
const expectedVersion: BootloaderVersion = {
protocolName: 'K',
major: 2,
minor: 0,
bugfix: 0
};
expect(version).toEqual(expectedVersion);
expect(testPeripheral.sendCommand).toHaveBeenCalledWith({
command: Commands.GetProperty,
params: [1, 0, 0, 0, 0, 0, 0, 0]
});
});
});
});

View File

@@ -0,0 +1,27 @@
import { CommandOption, CommandResponse, DataOption, Peripheral, ResponseCodes, ResponseTags } from '../src';
export class TestPeripheral implements Peripheral {
close(): void {
}
open(): void {
}
sendCommand(options: CommandOption): Promise<CommandResponse> {
const response = {
tag: ResponseTags.Generic,
code: ResponseCodes.Success,
raw: Buffer.alloc(0)
};
return Promise.resolve(response);
}
writeMemory(data: DataOption): Promise<void> {
return Promise.resolve();
}
readMemory(startAddress: number, count: number): Promise<Buffer> {
return Promise.resolve(Buffer.alloc(0));
}
}

View File

@@ -0,0 +1,33 @@
import { execSync } from 'child_process';
import { join } from 'path';
import { readFileSync } from 'fs';
import * as MemoryMap from 'nrf-intel-hex';
export enum UhkReenumerationModes {
Bootloader = 'bootloader',
Buspal = 'buspal',
NormalKeyboard = 'normalKeyboard',
CompatibleKeyboard = 'compatibleKeyboard'
}
const USB_SCRIPTS_DIR = join(__dirname, '../../../usb');
export const reenumerate = (mode: UhkReenumerationModes): void => {
const reenumerateScriptFile = join(USB_SCRIPTS_DIR, 'reenumerate.js');
const command = [reenumerateScriptFile, mode.toString()].join(' ');
execSync(
command,
{
cwd: USB_SCRIPTS_DIR,
stdio: [0, 1, 2]
}
);
};
export const readBootloaderFirmwareFromHexFile = (): Map<any, any> => {
const hexFilePath = join(__dirname, '../../../../tmp/packages/firmware/devices/uhk60-right/firmware.hex');
const fileContent = readFileSync(hexFilePath, { encoding: 'utf8' });
const memoryMap = MemoryMap.fromHex(fileContent);
return memoryMap;
};

View File

@@ -0,0 +1,10 @@
import { encodeStringToParams } from '../../src/util';
describe('encodeStringToParams', () => {
xit('should convert 8 character to little endian 4 byte array', () => {
const result = encodeStringToParams('0403020108070605');
const expectedResult = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
expect(result).toEqual(expectedResult);
});
});

View File

@@ -0,0 +1,17 @@
import { getResponseCode, ResponseCodes } from '../../src';
describe('response-parser', () => {
describe('getResponseCode', () => {
it('should return with success', () => {
const buffer = Buffer.from([0x03, 0x00, 0x08, 0x00, 0xa7, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]);
const responseCode = getResponseCode(buffer);
expect(responseCode).toEqual(ResponseCodes.Success);
});
it('should return with UnknownProperty', () => {
const buffer = Buffer.from([0x03, 0x00, 0x08, 0x00, 0xa7, 0x00, 0x00, 0x01, 0x3c, 0x28, 0x00, 0x00]);
const responseCode = getResponseCode(buffer);
expect(responseCode).toEqual(ResponseCodes.UnknownProperty);
});
});
});

View File

@@ -0,0 +1,13 @@
import { decodeCommandResponse } from '../../../src/util/usb/decode-command-response';
describe('decodeCommandResponse', () => {
it('should parse the command', () => {
const arr = '03 00 0c 00 a0 00 00 02 00 00 00 00 c1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'
.split(' ');
const buffer = Buffer.from(arr);
const response = decodeCommandResponse(buffer);
expect(response.code).toEqual(0);
expect(response.tag).toEqual(0);
});
});

View File

@@ -0,0 +1,16 @@
import { CommandOption, Commands } from '../../../src';
import { encodeCommandOption } from '../../../src/util';
describe('usb encodeCommandOption', () => {
it('should convert correctly', () => {
const option: CommandOption = {
command: Commands.GetProperty,
params: [1, 0, 0, 0, 0, 0, 0, 0]
};
const result = encodeCommandOption(option);
// tslint:disable-next-line:max-line-length
const expected = [1, 0, 0x0c, 0, 0x07, 0x00, 0x00, 0x02, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
expect(result).toEqual(expected);
});
});

View File

@@ -0,0 +1,81 @@
import { reenumerate, UhkReenumerationModes, readBootloaderFirmwareFromHexFile } from './uhk-helpers';
import { BootloaderVersion, UsbPeripheral } from '../src';
import { KBoot } from '../src/kboot';
import { DataOption } from '../index';
xdescribe('UHK Integration tests', () => {
describe('bootloader', () => {
let usb: UsbPeripheral;
let kboot: KBoot;
beforeEach(() => {
reenumerate(UhkReenumerationModes.Bootloader);
usb = new UsbPeripheral({ vendorId: 0x1d50, productId: 0x6120 });
kboot = new KBoot(usb);
});
afterEach(() => {
if (usb) {
usb.close();
}
if (kboot) {
kboot.close();
}
});
it('get bootloader version', async () => {
const expectedVersion: BootloaderVersion = {
protocolName: 'K',
major: 2,
minor: 0,
bugfix: 0
};
const version = await kboot.getBootloaderVersion();
expect(version).toEqual(expectedVersion);
});
it('disable flash security', () => {
const backdoorKey = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
return kboot
.flashSecurityDisable(backdoorKey)
.catch(err => {
expect(err).toBeFalsy();
});
});
it('flash erase region', () => {
return kboot
.flashEraseRegion(0xc000, 475136)
.catch(err => {
expect(err).toBeFalsy();
});
});
it('read memory', () => {
const dataLength = 128;
return kboot
.readMemory(0xc000, dataLength)
.then((data: number[]) => {
expect(data).toBeTruthy();
expect(data.length).toEqual(dataLength);
})
.catch(err => {
expect(err).toBeFalsy();
});
});
it('write memory', async () => {
const bootloaderMemoryMap = readBootloaderFirmwareFromHexFile();
for (const [startAddress, data] of bootloaderMemoryMap.entries()) {
const dataOption: DataOption = {
startAddress,
data
};
await kboot.writeMemory(dataOption);
}
});
});
});

View File

@@ -0,0 +1,6 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,18 +14,12 @@
"npm": ">=5.1.0 <6.0.0"
},
"dependencies": {
},
"devDependencies": {
"@types/jasmine": "2.6.0",
"@types/node": "8.0.30",
"jasmine": "2.8.0",
"jasmine-core": "2.8.0",
"jasmine-node": "2.0.0",
"jasmine-ts": "0.2.1",
"ts-node": "3.3.0",
"uhk-common": "1.0.0"
},
"devDependencies": {
},
"scripts": {
"test": "jasmine-ts --config=jasmine.json"
"test": "jasmine-ts --config=jasmine.json",
"lint": "tslint --project tsconfig.json"
}
}

View File

@@ -1,11 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"typeRoots": [
"./node_modules/@types"
]
"experimentalDecorators": true
}
}

View File

@@ -1,28 +0,0 @@
// var webpack = require("webpack");
module.exports = {
entry: {
main: __dirname + '/test-serializer.ts'
},
target: 'node',
output: {
path: __dirname,
filename: "test-serializer.js"
},
resolve: {
extensions: ['.webpack.js', '.web.js', '.ts', '.js'],
modules: ['node_modules']
},
module: {
rules: [
{ test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/ }
]
},
plugins: [
// new webpack.optimize.UglifyJsPlugin({ minimize: true }),
],
node: {
fs: "empty"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,24 +17,24 @@
"command-line-args": "4.0.7",
"decompress": "4.2.0",
"decompress-bzip2": "4.0.0",
"node-hid": "0.5.7",
"node-hid": "0.7.9",
"sudo-prompt": "7.0.0",
"tmp": "0.0.33",
"tslib": "1.10.0",
"uhk-common": "^1.0.0",
"uhk-usb": "^1.0.0"
},
"devDependencies": {
"@types/decompress": "4.2.0",
"@types/node": "8.0.33",
"@types/tmp": "0.0.33"
},
"scripts": {
"start": "electron ./dist/electron-main.js",
"start": "cross-env DEBUG=kboot* electron ./dist/electron-main.js",
"electron:spe": "electron ./dist/electron-main.js --spe",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost",
"electron:kboot": "cross-env DEBUG=kboot* electron ./dist/electron-main.js --useKboot",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-to-tmp-folder",
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
"lint": "tslint --project tsconfig.json",
"install:build-deps": "cd ./dist && npm i",
"download-firmware": "node ../../scripts/download-firmware.js",
"copy-blhost": "node ../../scripts/copy-blhost.js"
"copy-to-tmp-folder": "node ../../scripts/copy-to-tmp-folder.js"
}
}

View File

@@ -11,17 +11,20 @@ import * as commandLineArgs from 'command-line-args';
import { UhkHidDevice, UhkOperations } from 'uhk-usb';
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
import { CommandLineArgs, LogRegExps } from 'uhk-common';
import { UhkBlhost } from 'uhk-usb';
import { DeviceService } from './services/device.service';
import { logger } from './services/logger.service';
import { AppUpdateService } from './services/app-update.service';
import { AppService } from './services/app.service';
import { SudoService } from './services/sudo.service';
import { UhkBlhost } from '../../uhk-usb/src';
import * as isDev from 'electron-is-dev';
import { setMenu } from './electron-menu';
import { loadWindowState, saveWindowState } from './util/window';
const optionDefinitions = [
{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);
@@ -60,7 +63,13 @@ if (console.debug) {
};
}
const isSecondInstance = !app.requestSingleInstanceLock();
function createWindow() {
if (isSecondInstance) {
return;
}
logger.info('[Electron Main] Create new window.');
let packagesDir;
if (isDev) {
@@ -71,22 +80,32 @@ function createWindow() {
logger.info(`[Electron Main] packagesDir: ${packagesDir}`);
const loadedWindowState = loadWindowState();
// Create the browser window.
win = new BrowserWindow({
title: 'UHK Agent',
width: 1024,
height: 768,
x: loadedWindowState.x,
y: loadedWindowState.y,
width: loadedWindowState.width,
height: loadedWindowState.height,
webPreferences: {
nodeIntegration: true
},
icon: path.join(__dirname, 'renderer/assets/images/agent-app-icon.png')
});
win.setMenuBarVisibility(false);
win.maximize();
uhkHidDeviceService = new UhkHidDevice(logger, options);
if (loadedWindowState.isFullScreen) {
win.setFullScreen(true);
} else if (loadedWindowState.isMaximized) {
win.maximize();
}
setMenu(win);
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
uhkBlhost = new UhkBlhost(logger, packagesDir);
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir, options);
appUpdateService = new AppUpdateService(logger, win, app);
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
sudoService = new SudoService(logger, options);
@@ -103,13 +122,13 @@ function createWindow() {
});
// Emitted when the window is closed.
win.on('closed', () => {
win.on('closed', async () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
logger.info('[Electron Main] win closed');
win = null;
deviceService.close();
await deviceService.close();
deviceService = null;
appUpdateService = null;
appService = null;
@@ -124,31 +143,49 @@ function createWindow() {
win.webContents.on('crashed', (event: any) => {
logger.error(event);
});
win.on('close', () => saveWindowState(win));
win.on('resize', () => saveWindowState(win));
win.on('move', () => saveWindowState(win));
}
if (isSecondInstance) {
app.quit();
} else {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', () => {
app.quit();
});
app.on('window-all-closed', () => {
if (appUpdateService) {
appUpdateService.saveFirtsRun();
}
app.exit();
});
app.on('will-quit', () => {
if (appUpdateService) {
appUpdateService.saveFirtsRun();
}
});
app.on('will-quit', () => {
});
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});
app.on('second-instance', () => {
// Someone tried to run a second instance, we should focus our window.
if (win) {
if (win.isMinimized()) {
win.restore();
}
win.focus();
}
});
}
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here

View File

@@ -0,0 +1,37 @@
import { app, BrowserWindow, Menu, MenuItemConstructorOptions, systemPreferences } from 'electron';
import * as isDev from 'electron-is-dev';
export const setMenu = (win: BrowserWindow): void => {
if (process.platform !== 'darwin' || isDev) {
win.setMenuBarVisibility(false);
return;
}
const template: MenuItemConstructorOptions[] = [
{
label: app.getName(),
submenu: [
{role: 'quit'}
]
},
{
label: 'Edit',
submenu: [
{role: 'cut'},
{role: 'copy'},
{role: 'paste'},
{role: 'delete'},
{role: 'selectall'}
]
}
];
// hide "Start Dictation" submenu item in Edit menu
systemPreferences.setUserDefault('NSDisabledDictationMenuItem', 'boolean', true as any);
// hide "Emoji & Symbols" submenu item in Edit menu
systemPreferences.setUserDefault('NSDisabledCharacterPaletteMenuItem', 'boolean', false as any);
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
};

View File

@@ -3,5 +3,6 @@ import { SynchrounousResult } from 'tmp';
export interface TmpFirmware {
rightFirmwarePath: string;
leftFirmwarePath: string;
packageJsonPath: string;
tmpDirectory: SynchrounousResult;
}

View File

@@ -0,0 +1,6 @@
import { Rectangle } from 'electron';
export interface WindowState extends Rectangle {
isMaximized: boolean;
isFullScreen: boolean;
}

View File

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

View File

@@ -5,10 +5,13 @@ import * as settings from 'electron-settings';
import * as isDev from 'electron-is-dev';
import * as storage from 'electron-settings';
import { IpcEvents, LogService } from 'uhk-common';
import { AutoUpdateSettings, IpcEvents, LogService } from 'uhk-common';
import { MainServiceBase } from './main-service-base';
export class AppUpdateService extends MainServiceBase {
private sendAutoUpdateNotification = false;
constructor(protected logService: LogService,
protected win: Electron.BrowserWindow,
private app: Electron.App) {
@@ -24,16 +27,21 @@ export class AppUpdateService extends MainServiceBase {
private initListeners() {
autoUpdater.on('checking-for-update', () => {
this.logService.debug('[AppUpdateService] checking for update');
this.sendIpcToWindow(IpcEvents.autoUpdater.checkingForUpdate);
});
autoUpdater.on('update-available', async (ev: any, info: UpdateInfo) => {
this.logService.debug('[AppUpdateService] update available. Downloading started');
await autoUpdater.downloadUpdate();
this.sendIpcToWindow(IpcEvents.autoUpdater.updateAvailable, info);
});
autoUpdater.on('update-not-available', (ev: any, info: UpdateInfo) => {
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
if (this.sendAutoUpdateNotification) {
this.logService.debug('[AppUpdateService] update not available');
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
}
});
autoUpdater.on('error', (ev: any, err: string) => {
@@ -51,6 +59,7 @@ export class AppUpdateService extends MainServiceBase {
});
autoUpdater.on('update-downloaded', (ev: any, info: UpdateInfo) => {
this.logService.debug('[AppUpdateService] update downloaded');
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateDownloaded, info);
});
@@ -61,32 +70,46 @@ export class AppUpdateService extends MainServiceBase {
ipcMain.on(IpcEvents.app.appStarted, () => {
if (this.checkForUpdateAtStartup()) {
this.sendAutoUpdateNotification = false;
this.logService.debug('[AppUpdateService] app started. Automatically check for update.');
this.checkForUpdate();
}
});
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => {
this.logService.debug('[AppUpdateService] checkForUpdate request from renderer process');
this.checkForUpdate();
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, (event: Electron.Event, args: any[]) => {
const allowPrerelease: boolean = args[0];
// tslint:disable-next-line:max-line-length
const logMsg = `[AppUpdateService] checkForUpdate request from renderer process. Allow prerelease: ${allowPrerelease}`;
this.logService.debug(logMsg);
this.sendAutoUpdateNotification = true;
this.checkForUpdate(allowPrerelease);
});
}
private checkForUpdate() {
private checkForUpdate(allowPrerelease = false): void {
if (isDev) {
const msg = '[AppUpdateService] Application update is not working in dev mode.';
this.logService.info(msg);
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
if (this.sendAutoUpdateNotification) {
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
}
return;
}
if (this.isFirstRun()) {
const msg = '[AppUpdateService] Application update is skipping at first run.';
this.logService.info(msg);
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
if (this.sendAutoUpdateNotification) {
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
}
return;
}
autoUpdater.allowPrerelease = this.allowPreRelease();
autoUpdater.allowPrerelease = allowPrerelease;
autoUpdater.checkForUpdates()
.then(() => {
this.logService.debug('[AppUpdateService] checkForUpdate success');
@@ -107,12 +130,6 @@ export class AppUpdateService extends MainServiceBase {
return firstRunVersion !== this.app.getVersion();
}
private allowPreRelease() {
const autoUpdateSettings = this.getAutoUpdateSettings();
return autoUpdateSettings && autoUpdateSettings.usePreReleaseUpdate;
}
private checkForUpdateAtStartup() {
const autoUpdateSettings = this.getAutoUpdateSettings();
const checkForUpdate = autoUpdateSettings && autoUpdateSettings.checkForUpdateOnStartUp;
@@ -122,10 +139,10 @@ export class AppUpdateService extends MainServiceBase {
return checkForUpdate;
}
private getAutoUpdateSettings() {
private getAutoUpdateSettings(): AutoUpdateSettings {
const value = storage.get('auto-update-settings');
if (!value) {
return {checkForUpdateOnStartUp: false, usePreReleaseUpdate: false};
return {checkForUpdateOnStartUp: false};
}
return JSON.parse(<string>value);

View File

@@ -1,5 +1,6 @@
import { BrowserWindow, ipcMain, shell } from 'electron';
import { ipcMain, shell } from 'electron';
import { UhkHidDevice } from 'uhk-usb';
import * as os from 'os';
import { AppStartInfo, IpcEvents, LogService } from 'uhk-common';
import { MainServiceBase } from './main-service-base';
@@ -22,13 +23,14 @@ export class AppService extends MainServiceBase {
private async handleAppStartInfo(event: Electron.Event) {
this.logService.info('[AppService] getAppStartInfo');
const deviceConnectionState = await this.uhkHidDeviceService.getDeviceConnectionStateAsync();
const response: AppStartInfo = {
deviceConnectionState,
commandLineArgs: {
addons: this.options.addons || false
},
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
hasPermission: this.uhkHidDeviceService.hasPermission()
platform: process.platform as string,
osVersion: os.release()
};
this.logService.info('[AppService] getAppStartInfo response:', response);
return event.sender.send(IpcEvents.app.getAppStartInfoReply, response);

View File

@@ -1,30 +1,31 @@
import { ipcMain } from 'electron';
import { isEqual } from 'lodash';
import {
CommandLineArgs,
ConfigurationReply,
DeviceConnectionState,
FirmwareUpgradeIpcResponse,
getHardwareConfigFromDeviceResponse,
HardwareModules,
IpcEvents,
IpcResponse,
LogService,
mapObjectToUserConfigBinaryBuffer,
SaveUserConfigurationData
SaveUserConfigurationData,
UpdateFirmwareData
} from 'uhk-common';
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { emptyDir } from 'fs-extra';
import * as path from 'path';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/distinctUntilChanged';
import { saveTmpFirmware } from '../util/save-extract-firmware';
import { TmpFirmware } from '../models/tmp-firmware';
import { QueueManager } from './queue-manager';
import { backupUserConfiguration, getBackupUserConfigurationContent } from '../util/backup-user-confoguration';
import {
backupUserConfiguration,
getBackupUserConfigurationContent,
getPackageJsonFromPathAsync,
saveTmpFirmware
} from '../util';
/**
* IpcMain pair of the UHK Communication
@@ -33,14 +34,21 @@ import { backupUserConfiguration, getBackupUserConfigurationContent } from '../u
* - Read UserConfiguration from the UHK Device
*/
export class DeviceService {
private pollTimer$: Subscription;
private _pollerAllowed: boolean;
private _uhkDevicePolling: boolean;
private queueManager = new QueueManager();
constructor(private logService: LogService,
private win: Electron.BrowserWindow,
private device: UhkHidDevice,
private operations: UhkOperations) {
this.pollUhkDevice();
private operations: UhkOperations,
private rootDir: string,
private options: CommandLineArgs) {
this.startPollUhkDevice();
this.uhkDevicePoller()
.catch(error => {
this.logService.error('[DeviceService] UHK Device poller error', error);
});
ipcMain.on(IpcEvents.device.saveUserConfiguration, (...args: any[]) => {
this.queueManager.add({
@@ -69,7 +77,25 @@ export class DeviceService {
});
});
ipcMain.on(IpcEvents.device.startConnectionPoller, this.pollUhkDevice.bind(this));
ipcMain.on(IpcEvents.device.startConnectionPoller, this.startPollUhkDevice.bind(this));
ipcMain.on(IpcEvents.device.recoveryDevice, (...args: any[]) => {
this.queueManager.add({
method: this.recoveryDevice,
bind: this,
params: args,
asynchronous: true
});
});
ipcMain.on(IpcEvents.device.enableUsbStackTest, (...args: any[]) => {
this.queueManager.add({
method: this.enableUsbStackTest,
bind: this,
params: args,
asynchronous: true
});
});
logService.debug('[DeviceService] init success');
}
@@ -82,12 +108,11 @@ export class DeviceService {
let response: ConfigurationReply;
try {
await this.stopPollUhkDevice();
await this.device.waitUntilKeyboardBusy();
const result = await this.operations.loadConfigurations();
const modules: HardwareModules = {
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
};
const modules: HardwareModules = await this.getHardwareModules(false);
const hardwareConfig = getHardwareConfigFromDeviceResponse(result.hardwareConfiguration);
const uniqueId = hardwareConfig.uniqueId;
@@ -105,38 +130,88 @@ export class DeviceService {
};
} finally {
this.device.close();
this.startPollUhkDevice();
}
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
}
public close(): void {
this.stopPollTimer();
public async getHardwareModules(catchError: boolean): Promise<HardwareModules> {
try {
await this.device.waitUntilKeyboardBusy();
return {
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
};
} catch (err) {
if (!catchError) {
return err;
}
this.logService.error('[DeviceService] Read hardware modules information failed', err);
return {
leftModuleInfo: {},
rightModuleInfo: {}
};
}
}
public async close(): Promise<void> {
await this.stopPollUhkDevice();
this.logService.info('[DeviceService] Device connection checker stopped.');
}
public async updateFirmware(event: Electron.Event, args?: Array<string>): Promise<void> {
const response = new IpcResponse();
const response = new FirmwareUpgradeIpcResponse();
const data: UpdateFirmwareData = JSON.parse(args[0]);
let firmwarePathData: TmpFirmware;
try {
this.stopPollTimer();
this.logService.debug('Agent version:', data.versionInformation.version);
const hardwareModules = await this.getHardwareModules(false);
this.logService.debug('Device right firmware version:', hardwareModules.rightModuleInfo.firmwareVersion);
this.logService.debug('Device left firmware version:', hardwareModules.leftModuleInfo.firmwareVersion);
if (args && args.length > 0) {
firmwarePathData = await saveTmpFirmware(args[0]);
await this.operations.updateRightFirmware(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModule(firmwarePathData.leftFirmwarePath);
}
else {
await this.operations.updateRightFirmware();
await this.operations.updateLeftModule();
await this.stopPollUhkDevice();
this.device.resetDeviceCache();
if (data.firmware) {
firmwarePathData = await saveTmpFirmware(data.firmware);
const packageJson = await getPackageJsonFromPathAsync(firmwarePathData.packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
if (this.options.useKboot) {
await this.operations.updateRightFirmwareWithKboot(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModuleWithKboot(firmwarePathData.leftFirmwarePath);
} else {
await this.operations.updateRightFirmwareWithBlhost(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModuleWithBlhost(firmwarePathData.leftFirmwarePath);
}
} else {
const packageJsonPath = path.join(this.rootDir, 'packages/firmware/package.json');
const packageJson = await getPackageJsonFromPathAsync(packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
if (this.options.useKboot) {
await this.operations.updateRightFirmwareWithKboot();
await this.operations.updateLeftModuleWithKboot();
} else {
await this.operations.updateRightFirmwareWithBlhost();
await this.operations.updateLeftModuleWithBlhost();
}
}
response.success = true;
response.modules = await this.getHardwareModules(false);
} catch (error) {
const err = {message: error.message, stack: error.stack};
const err = { message: error.message, stack: error.stack };
this.logService.error('[DeviceService] updateFirmware error', err);
response.modules = await this.getHardwareModules(true);
response.error = err;
}
@@ -145,34 +220,89 @@ export class DeviceService {
}
await snooze(500);
this.startPollUhkDevice();
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
}
public async recoveryDevice(event: Electron.Event): Promise<void> {
const response = new FirmwareUpgradeIpcResponse();
try {
await this.stopPollUhkDevice();
if (this.options.useKboot) {
await this.operations.updateRightFirmwareWithKboot();
} else {
await this.operations.updateRightFirmwareWithBlhost();
}
response.modules = await this.getHardwareModules(false);
response.success = true;
} catch (error) {
const err = { message: error.message, stack: error.stack };
this.logService.error('[DeviceService] updateFirmware error', err);
response.modules = await this.getHardwareModules(true);
response.error = err;
}
await snooze(500);
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
}
public async enableUsbStackTest(event: Electron.Event) {
await this.device.enableUsbStackTest();
}
private startPollUhkDevice(): void {
this._pollerAllowed = true;
}
private async stopPollUhkDevice(): Promise<void> {
return new Promise<void>(async resolve => {
this._pollerAllowed = false;
while (true) {
if (!this._uhkDevicePolling) {
return resolve();
}
await snooze(100);
}
});
}
/**
* HID API not support device attached and detached event.
* This method check the keyboard is attached to the computer or not.
* Every second check the HID device list.
* The halves are connected and merged or not.
* Every 250ms check the HID device list.
* @private
*/
private pollUhkDevice(): void {
if (this.pollTimer$) {
return;
private async uhkDevicePoller(): Promise<void> {
let savedState: DeviceConnectionState;
while (true) {
if (this._pollerAllowed) {
this._uhkDevicePolling = true;
try {
const state = await this.device.getDeviceConnectionStateAsync();
if (!isEqual(state, savedState)) {
savedState = state;
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
}
} catch (err) {
this.logService.error('[DeviceService] Device connection state query error', err);
}
}
this._uhkDevicePolling = false;
await snooze(250);
}
this.pollTimer$ = Observable.interval(1000)
.startWith(0)
.map(() => this.device.deviceConnected())
.distinctUntilChanged()
.do((connected: boolean) => {
const response: DeviceConnectionState = {
connected,
hasPermission: this.device.hasPermission()
};
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
this.logService.info('[DeviceService] Device connection state changed to:', response);
})
.subscribe();
}
private async saveUserConfiguration(event: Electron.Event, args: Array<string>): Promise<void> {
@@ -180,32 +310,23 @@ export class DeviceService {
const data: SaveUserConfigurationData = JSON.parse(args[0]);
try {
await this.stopPollUhkDevice();
await backupUserConfiguration(data);
const buffer = mapObjectToUserConfigBinaryBuffer(data.configuration);
await this.operations.saveUserConfiguration(buffer);
response.success = true;
}
catch (error) {
} catch (error) {
this.logService.error('[DeviceService] Transferring error', error);
response.error = {message: error.message};
response.error = { message: error.message };
} finally {
this.device.close();
this.startPollUhkDevice();
}
event.sender.send(IpcEvents.device.saveUserConfigurationReply, response);
return Promise.resolve();
}
private stopPollTimer(): void {
if (!this.pollTimer$) {
return;
}
this.pollTimer$.unsubscribe();
this.pollTimer$ = null;
}
}

View File

@@ -0,0 +1,13 @@
import * as fs from 'fs';
export const getPackageJsonFromPathAsync = async (filePath: string): Promise<any> => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, {encoding: 'utf-8'}, (err, data) => {
if (err) {
return reject(err);
}
resolve(JSON.parse(data));
});
});
};

View File

@@ -0,0 +1,3 @@
export * from './backup-user-confoguration';
export * from './get-package-json-from-path-async';
export * from './save-extract-firmware';

View File

@@ -6,7 +6,7 @@ import * as decompressTarbz from 'decompress-tarbz2';
import { TmpFirmware } from '../models/tmp-firmware';
export async function saveTmpFirmware(data: string): Promise<TmpFirmware> {
export async function saveTmpFirmware(data: Array<number>): Promise<TmpFirmware> {
const tmpDirectory = dirSync();
const zipFilePath = path.join(tmpDirectory.name, 'firmware.bz2');
@@ -16,15 +16,14 @@ export async function saveTmpFirmware(data: string): Promise<TmpFirmware> {
return {
tmpDirectory,
rightFirmwarePath: path.join(tmpDirectory.name, 'devices/uhk60-right/firmware.hex'),
leftFirmwarePath: path.join(tmpDirectory.name, 'modules/uhk60-left.bin')
leftFirmwarePath: path.join(tmpDirectory.name, 'modules/uhk60-left.bin'),
packageJsonPath: path.join(tmpDirectory.name, 'package.json')
};
}
function writeDataToFile(data: string, filePath: string): Promise<void> {
function writeDataToFile(data: Array<number>, filePath: string): Promise<void> {
return new Promise((resolve, reject) => {
const array: Array<number> = JSON.parse(data);
const buffer = new Buffer(array);
const buffer = Buffer.from(data);
fs.writeFile(filePath, buffer, err => {
if (err) {

View File

@@ -0,0 +1,70 @@
import * as electron from 'electron';
import * as settings from 'electron-settings';
import { logger } from '../services/logger.service';
import { WindowState } from '../models/window-state';
const WINDOWS_SETTINGS_KEY = 'windowSettings';
export const windowVisibleFilter = (state: WindowState) => {
return (display: electron.Display): boolean => (
state.x >= display.bounds.x &&
state.y >= display.bounds.y &&
state.x <= display.bounds.width &&
state.y <= display.bounds.height
);
};
export const windowVisibleOnScreen = (state: WindowState): boolean => {
return electron.screen.getAllDisplays().some(windowVisibleFilter(state));
};
export const getDefaultWindowState = () => ({
width: 1024,
height: 768,
isMaximized: true
});
export const loadWindowState = (): Partial<WindowState> => {
logger.log('[WindowState] load settings');
try {
const loadedState = settings.get(WINDOWS_SETTINGS_KEY) as any;
logger.log('[WindowState] loaded settings', loadedState);
if (!loadedState) {
logger.log('[WindowState]save state not exists, use default');
return getDefaultWindowState();
}
const visible = windowVisibleOnScreen(loadedState);
logger.log('[WindowState] loaded settings is visible', visible);
if (visible) {
logger.log('[WindowState] return with loaded settings');
return loadedState;
}
} catch (err) {
logger.error('[WindowState] error when parsing loaded settings', err);
}
logger.log('[WindowState] return with default settings');
return getDefaultWindowState();
};
export const saveWindowState = (win: electron.BrowserWindow) => {
const winBounds = win.isMaximized() || win.isFullScreen()
? loadWindowState() as any
: win.getBounds();
const state: WindowState = {
...winBounds,
isMaximized: win.isMaximized(),
isFullScreen: win.isFullScreen()
};
logger.log('[WindowState] save settings:', state);
settings.set(WINDOWS_SETTINGS_KEY, state as any);
logger.log('[WindowState] save settings success');
};

View File

@@ -1,21 +1,7 @@
{
"compileOnSave": false,
"extends": "../../tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2016",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2015.iterable",
"es2016",
"dom",
"dom.iterable"
]
"baseUrl": "./src",
"declaration": false
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,8 @@
"private": true,
"version": "1.0.0",
"description": "Common Library contains the common code for uhk-agent (electron-main) and web (electron-renderer) modules",
"main": "dist/index.js",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"author": "Ultimate Gadget Laboratories",
"repository": {
"type": "git",
@@ -15,19 +16,14 @@
"copy:scancodes": "copyfiles ./src/config-serializer/config-items/scancodes.json dist",
"copy:secondary-roles": "copyfiles ./src/config-serializer/config-items/secondaryRole.json dist",
"test": "jasmine-ts --config=jasmine.json",
"coverage": "nyc jasmine-ts --config=jasmine.json"
"coverage": "nyc jasmine-ts --config=jasmine.json",
"lint": "tslint --project tsconfig.json"
},
"dependencies": {
"buffer": "5.2.1",
"tslib": "1.10.0"
},
"license": "GPL-3.0",
"devDependencies": {
"@types/jasmine": "2.6.0",
"@types/node": "8.0.30",
"jasmine": "2.8.0",
"jasmine-core": "2.8.0",
"jasmine-node": "2.0.0",
"jasmine-ts": "0.2.1",
"nyc": "11.2.1",
"ts-node": "3.3.0"
},
"nyc": {
"extension": [
".ts"

View File

@@ -41,16 +41,20 @@ export class HardwareConfiguration {
}
fromBinary(buffer: UhkBuffer): HardwareConfiguration {
this.signature = buffer.readString();
this.majorVersion = buffer.readUInt8();
this.minorVersion = buffer.readUInt8();
this.patchVersion = buffer.readUInt8();
this.brandId = buffer.readUInt8();
this.deviceId = buffer.readUInt8();
this.uniqueId = buffer.readUInt32();
this.isVendorModeOn = buffer.readBoolean();
this.isIso = buffer.readBoolean();
return this;
try {
this.signature = buffer.readString();
this.majorVersion = buffer.readUInt8();
this.minorVersion = buffer.readUInt8();
this.patchVersion = buffer.readUInt8();
this.brandId = buffer.readUInt8();
this.deviceId = buffer.readUInt8();
this.uniqueId = buffer.readUInt32();
this.isVendorModeOn = buffer.readBoolean();
this.isIso = buffer.readBoolean();
return this;
} catch (e) {
throw new Error('Please power cycle your keyboard (Invalid hardware configuration: Index out of bounds)');
}
}
toJsonObject(): any {

View File

@@ -8,6 +8,7 @@ export * from './secondary-role-action';
export * from './macro';
export * from './module';
export * from './module-configuration';
export * from './mouse-speed-configuration';
export * from './user-configuration';
export const SCANCODES = require('./scancodes.json');

View File

@@ -52,7 +52,7 @@ describe('keystroke-action', () => {
it('should not change the "type" to "shortMedia" when is "longMedia" if scancode < 256', () => {
const action = new KeystrokeAction();
action.type = KeystrokeType.longMedia;
action.type = KeystrokeType.longMedia as any;
action.scancode = 125;
expect(action.type).toEqual(KeystrokeType.shortMedia);
});
@@ -67,7 +67,7 @@ describe('keystroke-action', () => {
it('should not change the "type" to "longMedia" when is "shortMedia" if scancode >= 256', () => {
const action = new KeystrokeAction();
action.type = KeystrokeType.shortMedia;
action.type = KeystrokeType.shortMedia as any;
action.scancode = 256;
expect(action.type).toEqual(KeystrokeType.longMedia);
});
@@ -145,7 +145,7 @@ describe('keystroke-action', () => {
});
it('should change the value to "longMedia" if scancode >= 256 and value "shortMedia"', () => {
const value = KeystrokeType.shortMedia;
const value = KeystrokeType.shortMedia as any;
const scancode = 256;
const action = new KeystrokeAction();
action.scancode = scancode;
@@ -165,7 +165,7 @@ describe('keystroke-action', () => {
});
it('should change the value to "shortMedia" if scancode < 256 and value "longMedia"', () => {
const value = KeystrokeType.longMedia;
const value = KeystrokeType.longMedia as any;
const scancode = 100;
const action = new KeystrokeAction();
action.scancode = scancode;

View File

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

View File

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

View File

@@ -1,17 +1,18 @@
import { binaryDefaultHelper, jsonDefaultHelper } from '../../../../test/serializer-test-helper';
import { SwitchLayerAction } from './switch-layer-action';
import { SwitchLayerAction, SwitchLayerMode } from './switch-layer-action';
import { keyActionType } from './key-action';
// TODO: Add null, undefined, empty object, empty buffer test cases
describe('switch-layer-action', () => {
const action = new SwitchLayerAction(<SwitchLayerAction>{layer: 0, isLayerToggleable: false});
const action = new SwitchLayerAction(<SwitchLayerAction>{layer: 0, switchLayerMode: SwitchLayerMode.hold});
it('should be instantiate', () => {
expect(new SwitchLayerAction()).toBeTruthy();
});
describe('toString', () => {
it('should return <SwitchLayerAction layer="0" toggle="false">', () => {
expect(action.toString()).toEqual('<SwitchLayerAction layer="0" toggle="false">');
it('should return <SwitchLayerAction layer="0" switchLayerMode="hold">', () => {
expect(action.toString()).toEqual('<SwitchLayerAction layer="0" switchLayerMode="hold">');
});
});
@@ -30,4 +31,20 @@ describe('switch-layer-action', () => {
binaryDefaultHelper(action);
});
});
describe('backward compatibility of the "toggle" property ', () => {
it('should map toggle=false to SwitchLayerMode.holdAndDoubleTapToggle', () => {
const oldAction = new SwitchLayerAction();
oldAction.fromJsonObject({keyActionType: keyActionType.SwitchLayerAction, layer: 0, toggle: false});
expect(oldAction.switchLayerMode).toEqual(SwitchLayerMode.holdAndDoubleTapToggle);
});
it('should map toggle=true to SwitchLayerMode.toggle', () => {
const oldAction = new SwitchLayerAction();
oldAction.fromJsonObject({keyActionType: keyActionType.SwitchLayerAction, layer: 0, toggle: true});
expect(oldAction.switchLayerMode).toEqual(SwitchLayerMode.toggle);
});
});
});

View File

@@ -8,9 +8,48 @@ export enum LayerName {
mouse
}
export enum SwitchLayerMode {
holdAndDoubleTapToggle = 'holdAndDoubleTapToggle',
toggle = 'toggle',
hold = 'hold'
}
export const mapSwitchLayerModeToNumber = (switchLayerMode: SwitchLayerMode): number => {
switch (switchLayerMode) {
case SwitchLayerMode.holdAndDoubleTapToggle:
return 0;
case SwitchLayerMode.toggle:
return 1;
case SwitchLayerMode.hold:
return 2;
default:
throw new Error(`Can not map ${switchLayerMode} to number`);
}
};
export const mapNumberToSwitchLayerMode = (value: number): SwitchLayerMode => {
switch (value) {
case 0:
return SwitchLayerMode.holdAndDoubleTapToggle;
case 1:
return SwitchLayerMode.toggle;
case 2:
return SwitchLayerMode.hold;
default:
throw new Error(`Can not map "${value}" to SwitchLayerMode`);
}
};
export class SwitchLayerAction extends KeyAction {
isLayerToggleable: boolean;
@assertEnum(SwitchLayerMode)
switchLayerMode: SwitchLayerMode;
@assertEnum(LayerName)
layer: LayerName;
@@ -20,21 +59,29 @@ export class SwitchLayerAction extends KeyAction {
if (!other) {
return;
}
this.isLayerToggleable = other.isLayerToggleable;
this.switchLayerMode = other.switchLayerMode;
this.layer = other.layer;
}
fromJsonObject(jsonObject: any): SwitchLayerAction {
this.assertKeyActionType(jsonObject);
this.layer = LayerName[<string>jsonObject.layer];
this.isLayerToggleable = jsonObject.toggle;
// Backward compatibility when "switchLayerMode" was a boolean type as "toggle"
if (typeof jsonObject.toggle === 'boolean') {
this.switchLayerMode = jsonObject.toggle ? SwitchLayerMode.toggle : SwitchLayerMode.holdAndDoubleTapToggle;
}
else {
this.switchLayerMode = jsonObject.switchLayerMode;
}
return this;
}
fromBinary(buffer: UhkBuffer): SwitchLayerAction {
this.readAndAssertKeyActionId(buffer);
this.layer = buffer.readUInt8();
this.isLayerToggleable = buffer.readBoolean();
this.switchLayerMode = mapNumberToSwitchLayerMode(buffer.readUInt8());
return this;
}
@@ -42,18 +89,18 @@ export class SwitchLayerAction extends KeyAction {
return {
keyActionType: keyActionType.SwitchLayerAction,
layer: LayerName[this.layer],
toggle: this.isLayerToggleable
switchLayerMode: this.switchLayerMode
};
}
toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(KeyActionId.SwitchLayerAction);
buffer.writeUInt8(this.layer);
buffer.writeBoolean(this.isLayerToggleable);
buffer.writeUInt8(mapSwitchLayerModeToNumber(this.switchLayerMode));
}
toString(): string {
return `<SwitchLayerAction layer="${this.layer}" toggle="${this.isLayerToggleable}">`;
return `<SwitchLayerAction layer="${this.layer}" switchLayerMode="${this.switchLayerMode}">`;
}
public getName(): string {

View File

@@ -127,7 +127,7 @@ export class Keymap {
if (currentLayerId - 1 === baseKeyAction.layer) {
if (currentKeyAction instanceof SwitchLayerAction) {
if (currentKeyAction.layer === baseKeyAction.layer &&
currentKeyAction.isLayerToggleable === baseKeyAction.isLayerToggleable) {
currentKeyAction.switchLayerMode === baseKeyAction.switchLayerMode) {
continue;
}
// tslint:disable-next-line: max-line-length

View File

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

View File

@@ -109,7 +109,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -121,7 +121,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -151,7 +151,7 @@ describe('keymap', () => {
expect(inputUserConfig.toJsonObject()).toEqual(expectedJsonConfig);
// tslint:disable-next-line: max-line-length
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is not switch layer. <KeystrokeAction type="basic" scancode="44"> will be override with <SwitchLayerAction layer="0" toggle="false">');
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is not switch layer. <KeystrokeAction type="basic" scancode="44"> will be override with <SwitchLayerAction layer="0" switchLayerMode="holdAndDoubleTapToggle">');
});
it('should normalize SwitchLayerAction if non base layer action is other SwitchLayerAction', () => {
@@ -262,7 +262,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -274,7 +274,7 @@ describe('keymap', () => {
{
keyActionType: 'switchLayer',
layer: 'mod',
toggle: false
switchLayerMode: 'holdAndDoubleTapToggle'
}
]
}]
@@ -304,6 +304,6 @@ describe('keymap', () => {
expect(inputUserConfig.toJsonObject()).toEqual(expectedJsonConfig);
// tslint:disable-next-line: max-line-length
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is different switch layer. <SwitchLayerAction layer="1" toggle="false"> will be override with <SwitchLayerAction layer="0" toggle="false">');
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is different switch layer. <SwitchLayerAction layer="1" switchLayerMode="holdAndDoubleTapToggle"> will be override with <SwitchLayerAction layer="0" switchLayerMode="holdAndDoubleTapToggle">');
});
});

View File

@@ -3,12 +3,12 @@ import { UhkBuffer } from '../../uhk-buffer';
import { MacroAction, MacroActionId, MacroMouseSubAction, macroActionType } from './macro-action';
export enum MouseButtons {
Left = 1 << 0,
Middle = 1 << 1,
Right = 1 << 2
Left = 0,
Right = 1,
Middle = 2
}
interface JsObjectMouseButtonMacroAction {
export interface JsObjectMouseButtonMacroAction {
macroActionType: string;
action: string;
mouseButtonsMask?: number;

View File

@@ -0,0 +1,12 @@
export interface MouseSpeedConfiguration {
mouseMoveInitialSpeed: number;
mouseMoveAcceleration: number;
mouseMoveDeceleratedSpeed: number;
mouseMoveBaseSpeed: number;
mouseMoveAcceleratedSpeed: number;
mouseScrollInitialSpeed: number;
mouseScrollAcceleration: number;
mouseScrollDeceleratedSpeed: number;
mouseScrollBaseSpeed: number;
mouseScrollAcceleratedSpeed: number;
}

View File

@@ -259,7 +259,7 @@
},
{
"id": "70",
"text": "Print Screen"
"text": "Print Screen SysRq"
},
{
"id": "72",
@@ -454,14 +454,6 @@
"scancode": 182
}
},
{
"id": "132",
"text": "Stop/Eject",
"additional": {
"type": "media",
"scancode": 204
}
},
{
"id": "133",
"text": "Play/Pause",
@@ -503,11 +495,19 @@
}
},
{
"id": "138",
"text": "WWW",
"id": "145",
"text": "History Back",
"additional": {
"type": "media",
"scancode": 138
"scancode": 548
}
},
{
"id": "146",
"text": "History Forward",
"additional": {
"type": "media",
"scancode": 549
}
}
]
@@ -515,14 +515,6 @@
{
"text": "Launch application",
"children": [
{
"id": "142",
"text": "Launch Web Browser",
"additional": {
"type": "media",
"scancode": 406
}
},
{
"id": "143",
"text": "Launch Email Client",
@@ -687,5 +679,50 @@
"text": "."
}
]
},
{
"text": "International",
"children": [
{
"id": "235",
"text": "International 1",
"additional": {
"type": "basic",
"scancode": 135
}
},
{
"id": "236",
"text": "International 2",
"additional": {
"type": "basic",
"scancode": 136
}
},
{
"id": "237",
"text": "International 3",
"additional": {
"type": "basic",
"scancode": 137
}
},
{
"id": "244",
"text": "Language 1",
"additional": {
"type": "basic",
"scancode": 144
}
},
{
"id": "245",
"text": "Language 2",
"additional": {
"type": "basic",
"scancode": 145
}
}
]
}
]

View File

@@ -1,11 +1,15 @@
import { assertUInt8, assertUInt16 } from '../assert';
import { assertUInt16, assertUInt8 } from '../assert';
import { UhkBuffer } from '../uhk-buffer';
import { Keymap } from './keymap';
import { Macro } from './macro';
import { ModuleConfiguration } from './module-configuration';
import { ConfigSerializer } from '../config-serializer';
import { KeystrokeAction, NoneAction } from './key-action';
import { SecondaryRoleAction } from './secondary-role-action';
import { isScancodeExists } from './scancode-checker';
import { MouseSpeedConfiguration } from './mouse-speed-configuration';
export class UserConfiguration {
export class UserConfiguration implements MouseSpeedConfiguration {
@assertUInt16
userConfigMajorVersion: number;
@@ -90,7 +94,7 @@ export class UserConfiguration {
this.mouseMoveAcceleratedSpeed = jsonObject.mouseMoveAcceleratedSpeed;
this.mouseScrollInitialSpeed = jsonObject.mouseScrollInitialSpeed;
this.mouseScrollAcceleration = jsonObject.mouseScrollAcceleration;
this.mouseScrollDeceleratedSpeed = jsonObject.mouseScrollAcceleration;
this.mouseScrollDeceleratedSpeed = jsonObject.mouseScrollDeceleratedSpeed;
this.mouseScrollBaseSpeed = jsonObject.mouseScrollBaseSpeed;
this.mouseScrollAcceleratedSpeed = jsonObject.mouseScrollAcceleratedSpeed;
this.moduleConfigurations = jsonObject.moduleConfigurations.map((moduleConfiguration: any) => {
@@ -102,7 +106,9 @@ export class UserConfiguration {
return macro;
});
this.keymaps = jsonObject.keymaps.map((keymap: any) => new Keymap().fromJsonObject(keymap, this.macros));
this.clean();
this.recalculateConfigurationLength();
return this;
}
@@ -138,6 +144,8 @@ export class UserConfiguration {
this.keymaps = buffer.readArray<Keymap>(uhkBuffer => new Keymap().fromBinary(uhkBuffer, this.macros));
ConfigSerializer.resolveSwitchKeymapActions(this.keymaps);
this.clean();
if (this.userConfigurationLength === 0) {
this.recalculateConfigurationLength();
}
@@ -222,4 +230,34 @@ export class UserConfiguration {
this.deviceName = 'My UHK';
}
}
/* Remove not allowed settings/bugs
* 1. Layer Switcher secondary roles allowed only on base layers
*/
private clean(): void {
for (const keymap of this.keymaps) {
for (let layerId = 1; layerId < keymap.layers.length; layerId++) {
const layer = keymap.layers[layerId];
for (const module of layer.modules) {
for (let keyActionId = 0; keyActionId < module.keyActions.length; keyActionId++) {
const keyAction = module.keyActions[keyActionId];
if (!keyAction || !(keyAction instanceof KeystrokeAction)) {
continue;
}
if (keyAction.secondaryRoleAction === SecondaryRoleAction.fn ||
keyAction.secondaryRoleAction === SecondaryRoleAction.mod ||
keyAction.secondaryRoleAction === SecondaryRoleAction.mouse) {
(keyAction as any)._secondaryRoleAction = undefined;
}
if (keyAction.hasScancode() && !isScancodeExists(keyAction.scancode)) {
module.keyActions[keyActionId] = new NoneAction();
}
}
}
}
}
}
}

View File

@@ -1,3 +1,6 @@
// / need to load the buffer package from dependency instead of use node default buffer
import { Buffer } from 'buffer/';
export class UhkBuffer {
static simpleElementWriter<T>(buffer: UhkBuffer, element: T): void {
@@ -37,7 +40,7 @@ export class UhkBuffer {
constructor() {
this.offset = 0;
this.bytesToBacktrack = 0;
this.buffer = new Buffer(UhkBuffer.eepromSize);
this.buffer = Buffer.alloc(UhkBuffer.eepromSize);
this.buffer.fill(0);
}

View File

@@ -1,7 +1,9 @@
import { CommandLineArgs } from './command-line-args';
import { DeviceConnectionState } from './device-connection-state';
export interface AppStartInfo {
commandLineArgs: CommandLineArgs;
deviceConnected: boolean;
hasPermission: boolean;
deviceConnectionState: DeviceConnectionState;
platform: string;
osVersion: string;
}

View File

@@ -1,4 +1,4 @@
export interface AutoUpdateSettings {
checkForUpdateOnStartUp: boolean;
usePreReleaseUpdate: boolean;
usePreReleaseUpdate?: boolean;
}

View File

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

Some files were not shown because too many files have changed in this diff Show More