192 Commits

Author SHA1 Message Date
Róbert Kiss
f3e21ee9e4 Merge branch 'master' into chore-rebuild-uhk-web-package-lock 2019-08-23 23:19:15 +02:00
Róbert Kiss
0bd1152a32 chore: upgrade copyfiles => 2.1.1 (#1027) 2019-08-23 23:19:00 +02:00
Róbert Kiss
61ddb49b0f chore: rebuild the uhk-web package-lock.json to refresh deps of deps 2019-08-23 23:17:51 +02:00
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
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
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
Róbert Kiss
0914a1496b fix: confirmation popover titles (#1019) 2019-08-20 16:02:29 +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
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
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
Róbert Kiss
f6bef928fe chore(usb): add kboot-firmware-upgrade script (#1006) 2019-08-02 02:51:07 +02:00
Róbert Kiss
5ab5ad5f0c feat: ESC cancel the keymap description editing (#1007) 2019-07-31 10:03:29 +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
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
371 changed files with 28602 additions and 27646 deletions

2
.nvmrc
View File

@@ -1 +1 @@
8.11.2
12.0.0

View File

@@ -6,6 +6,93 @@ 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.
- Don't disable input in the key action popover after adding a layer switch action, deleting it, and trying to edit it on its layer.
- Don't 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

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

@@ -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

19827
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,8 +3,8 @@
"private": true,
"author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js",
"version": "1.2.6",
"firmwareVersion": "8.4.0",
"version": "1.2.13",
"firmwareVersion": "8.5.4",
"deviceProtocolVersion": "4.4.0",
"userConfigVersion": "4.0.1",
"hardwareConfigVersion": "1.0.0",
@@ -15,60 +15,71 @@
},
"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/jquery": "3.3.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": "4.0.1",
"@types/lodash-es": "4.17.0",
"@types/lodash": "4.14.136",
"@types/node": "8.0.53",
"@types/node-hid": "0.5.2",
"@types/node-hid": "0.7.0",
"@types/request": "2.0.8",
"@types/usb": "1.1.3",
"@types/semver": "5.5.0",
"@types/tmp": "0.0.33",
"autoprefixer": "6.5.3",
"buffer": "5.0.6",
"check-node-version": "^3.2.0",
"copy-webpack-plugin": "4.0.1",
"copyfiles": "^2.0.0",
"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",
"devtron": "1.4.0",
"electron": "1.8.4",
"electron-builder": "20.15.0",
"electron": "5.0.9",
"electron-builder": "20.34.0",
"electron-debug": "1.5.0",
"electron-devtools-installer": "2.2.3",
"electron-log": "2.2.16",
"electron-rebuild": "1.8.1",
"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",
"gh-pages": "1.1.0",
"fs-extra": "8.1.0",
"gh-pages": "2.0.1",
"html-webpack-plugin": "3.2.0",
"jasmine": "3.4.0",
"jasmine-core": "3.4.0",
"jasmine-node": "3.0.0",
"jasmine-ts": "0.3.0",
"jsonfile": "4.0.0",
"lerna": "2.9.0",
"lodash-es": "4.17.4",
"mkdirp": "0.5.1",
"node-hid": "0.5.7",
"lerna": "3.16.4",
"lodash": "4.17.15",
"node-hid": "0.7.9",
"npm-run-all": "4.0.2",
"nrf-intel-hex": "1.3.0",
"postcss-url": "8.0.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.6.2",
"webpack": "3.10.0"
"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"
@@ -76,37 +87,21 @@
"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",
"test": "lerna run test",
"lint": "lerna run lint",
"e2e": "lerna run e2e --scope uhk-web",
"prebuild": "check-node-version --package",
"build": "run-s build:common build:usb build:web build:electron",
"build:web": "lerna exec --scope uhk-web npm run build",
"build:electron": "cross-env AOT_BUILD=true run-s -sn build:electron:renderer build:electron:main",
"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",
"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",
"pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites",
"release": "node ./scripts/release.js",
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist",
"predeploy-gh-pages": "run-s build:web",
"deploy-gh-pages": "gh-pages -d packages/uhk-web/dist"
},
"dependencies": {}
"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';

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

@@ -0,0 +1,194 @@
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 {
this.peripheral.open();
}
close(): void {
this.peripheral.close();
}
// ================= Read properties ==================
async getProperty(property: Properties, memoryId = MemoryIds.Internal): Promise<CommandResponse> {
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) {
throw new Error('Response tag is not property response');
}
if (response.code === ResponseCodes.UnknownProperty) {
throw new Error('Unknown property!');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Unknown error. Error code:${response.code}`);
}
return response;
}
async getBootloaderVersion(): Promise<BootloaderVersion> {
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> {
if (key.length !== 8) {
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) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Can not disable flash security`);
}
}
async flashEraseRegion(startAddress: number, count: number): Promise<void> {
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) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Can not disable flash security`);
}
}
async flashEraseAllUnsecure(): Promise<void> {
const command: CommandOption = {
command: Commands.FlashEraseAllUnsecure,
params: []
};
const response = await this.peripheral.sendCommand(command);
if (response.tag !== ResponseTags.Generic) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
throw new Error(`Can not disable flash security`);
}
}
async readMemory(startAddress: number, count: number): Promise<any> {
return this.peripheral.readMemory(startAddress, count);
}
async writeMemory(options: DataOption): Promise<void> {
return this.peripheral.writeMemory(options);
}
/**
* Reset the bootloader
*/
async reset(): Promise<void> {
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) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
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> {
if (address > 127) {
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) {
throw new Error('Response tag is not generic response');
}
if (response.code !== ResponseCodes.Success) {
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,275 @@
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 = 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> {
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> {
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,23 @@
"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",
"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

@@ -16,14 +16,13 @@ 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
// show 'Lock layer when double tapping this key' checkbox on 'Layer' tab of the config popover
{name: 'layer-double-tap', type: Boolean}
{name: 'spe', type: Boolean} // simulate privilege escalation error
];
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
@@ -37,7 +36,6 @@ let win: Electron.BrowserWindow;
autoUpdater.logger = logger;
let deviceService: DeviceService;
let uhkBlhost: UhkBlhost;
let uhkHidDeviceService: UhkHidDevice;
let uhkOperations: UhkOperations;
let appUpdateService: AppUpdateService;
@@ -62,19 +60,7 @@ if (console.debug) {
};
}
const isSecondInstance = app.makeSingleInstance(function (commandLine, workingDirectory) {
// Someone tried to run a second instance, we should focus our window.
if (win) {
if (win.isMinimized()) {
win.restore();
}
win.focus();
}
});
if (isSecondInstance) {
app.quit();
}
const isSecondInstance = !app.requestSingleInstanceLock();
function createWindow() {
if (isSecondInstance) {
@@ -91,21 +77,30 @@ 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);
uhkBlhost = new UhkBlhost(logger, packagesDir);
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
if (loadedWindowState.isFullScreen) {
win.setFullScreen(true);
} else if (loadedWindowState.isMaximized) {
win.maximize();
}
setMenu(win);
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
uhkOperations = new UhkOperations(logger, uhkHidDeviceService, packagesDir);
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir);
appUpdateService = new AppUpdateService(logger, win, app);
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
@@ -123,13 +118,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;
@@ -144,31 +139,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', () => {
if (appUpdateService) {
appUpdateService.saveFirtsRun();
}
app.exit();
});
app.on('window-all-closed', () => {
if (appUpdateService) {
appUpdateService.saveFirtsRun();
}
app.exit();
});
app.on('will-quit', () => {
});
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

@@ -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,7 +5,7 @@ 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 {
@@ -76,14 +76,17 @@ export class AppUpdateService extends MainServiceBase {
}
});
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => {
this.logService.debug('[AppUpdateService] checkForUpdate request from renderer process');
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();
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);
@@ -106,7 +109,7 @@ export class AppUpdateService extends MainServiceBase {
return;
}
autoUpdater.allowPrerelease = this.allowPreRelease();
autoUpdater.allowPrerelease = allowPrerelease;
autoUpdater.checkForUpdates()
.then(() => {
this.logService.debug('[AppUpdateService] checkForUpdate success');
@@ -127,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;
@@ -142,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

@@ -23,15 +23,12 @@ export class AppService extends MainServiceBase {
private async handleAppStartInfo(event: Electron.Event) {
this.logService.info('[AppService] getAppStartInfo');
const deviceConnectionState = this.uhkHidDeviceService.getDeviceConnectionState();
const deviceConnectionState = await this.uhkHidDeviceService.getDeviceConnectionStateAsync();
const response: AppStartInfo = {
deviceConnectionState,
commandLineArgs: {
addons: this.options.addons || false,
layerDoubleTap: this.options['layer-double-tap'] || false
addons: this.options.addons || false
},
deviceConnected: deviceConnectionState.connected,
hasPermission: deviceConnectionState.hasPermission,
bootloaderActive: deviceConnectionState.bootloaderActive,
platform: process.platform as string,
osVersion: os.release()
};

View File

@@ -1,4 +1,5 @@
import { ipcMain } from 'electron';
import { isEqual } from 'lodash';
import {
ConfigurationReply,
DeviceConnectionState,
@@ -9,20 +10,13 @@ import {
IpcResponse,
LogService,
mapObjectToUserConfigBinaryBuffer,
SaveUserConfigurationData
SaveUserConfigurationData,
UpdateFirmwareData
} from 'uhk-common';
import { deviceConnectionStateComparer, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
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 { TmpFirmware } from '../models/tmp-firmware';
import { QueueManager } from './queue-manager';
import {
@@ -39,7 +33,8 @@ import {
* - 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,
@@ -47,7 +42,11 @@ export class DeviceService {
private device: UhkHidDevice,
private operations: UhkOperations,
private rootDir: string) {
this.pollUhkDevice();
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({
@@ -76,7 +75,7 @@ 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({
@@ -107,6 +106,8 @@ export class DeviceService {
let response: ConfigurationReply;
try {
await this.stopPollUhkDevice();
await this.device.waitUntilKeyboardBusy();
const result = await this.operations.loadConfigurations();
const modules: HardwareModules = await this.getHardwareModules(false);
@@ -127,6 +128,7 @@ export class DeviceService {
};
} finally {
this.device.close();
this.startPollUhkDevice();
}
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
@@ -140,44 +142,49 @@ export class DeviceService {
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
};
}
catch (err) {
} catch (err) {
if (!catchError) {
return err;
}
this.logService.error('[DeviceService] Read hardware modules information failed', err);
return {
leftModuleInfo: {},
rightModuleInfo: {}
};
}
}
public close(): void {
this.stopPollTimer();
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 FirmwareUpgradeIpcResponse();
const data: UpdateFirmwareData = JSON.parse(args[0]);
let firmwarePathData: TmpFirmware;
try {
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);
await this.stopPollUhkDevice();
this.device.resetDeviceCache();
this.stopPollTimer();
if (args && args.length > 0) {
firmwarePathData = await saveTmpFirmware(args[0]);
if (data.firmware) {
firmwarePathData = await saveTmpFirmware(data.firmware);
const packageJson = await getPackageJsonFromPathAsync(firmwarePathData.packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
await this.operations.updateRightFirmware(firmwarePathData.rightFirmwarePath);
await this.operations.updateLeftModule(firmwarePathData.leftFirmwarePath);
}
else {
} else {
const packageJsonPath = path.join(this.rootDir, 'packages/firmware/package.json');
const packageJson = await getPackageJsonFromPathAsync(packageJsonPath);
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
@@ -189,7 +196,7 @@ export class DeviceService {
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);
@@ -202,7 +209,7 @@ export class DeviceService {
await snooze(500);
this.pollUhkDevice();
this.startPollUhkDevice();
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
}
@@ -211,18 +218,14 @@ export class DeviceService {
const response = new FirmwareUpgradeIpcResponse();
try {
this.stopPollTimer();
await this.stopPollUhkDevice();
await this.operations.updateRightFirmware();
await snooze(500);
this.pollUhkDevice();
response.modules = await this.getHardwareModules(false);
response.success = true;
} 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);
@@ -237,26 +240,51 @@ export class DeviceService {
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;
this.pollTimer$ = Observable.interval(1000)
.startWith(0)
.map(() => this.device.getDeviceConnectionState())
.distinctUntilChanged<DeviceConnectionState>(deviceConnectionStateComparer)
.do((state: DeviceConnectionState) => {
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
})
.subscribe();
while (true) {
if (this._pollerAllowed) {
this._uhkDevicePolling = true;
const state = await this.device.getDeviceConnectionStateAsync();
if (!isEqual(state, savedState)) {
savedState = state;
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
}
this._uhkDevicePolling = false;
}
await snooze(250);
}
}
private async saveUserConfiguration(event: Electron.Event, args: Array<string>): Promise<void> {
@@ -264,32 +292,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

@@ -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');
@@ -21,10 +21,9 @@ export async function saveTmpFirmware(data: string): Promise<TmpFirmware> {
};
}
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

@@ -16,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

@@ -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

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

View File

@@ -3,9 +3,9 @@ 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
}
export interface JsObjectMouseButtonMacroAction {

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,10 +1,9 @@
import { CommandLineArgs } from './command-line-args';
import { DeviceConnectionState } from './device-connection-state';
export interface AppStartInfo {
commandLineArgs: CommandLineArgs;
deviceConnected: boolean;
hasPermission: boolean;
bootloaderActive: 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,9 +7,4 @@ export interface CommandLineArgs {
* simulate privilege escalation error
*/
spe?: boolean;
/**
* show 'Lock layer when double tapping this key' checkbox on 'Layer' tab of the config popover
* if it false the checkbox invisible and the value of the checkbox = true
*/
layerDoubleTap?: boolean;
}

View File

@@ -1,5 +1,11 @@
import { UdevRulesInfo } from './udev-rules-info';
import { HalvesInfo } from './halves-info';
export interface DeviceConnectionState {
connected: boolean;
hasPermission: boolean;
bootloaderActive: boolean;
zeroInterfaceAvailable: boolean;
udevRulesInfo: UdevRulesInfo;
halvesInfo: HalvesInfo;
}

View File

@@ -0,0 +1,4 @@
export interface HalvesInfo {
areHalvesMerged: boolean;
isLeftHalfConnected: boolean;
}

View File

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

View File

@@ -1,3 +1,4 @@
export * from './auto-update-settings';
export * from './command-line-args';
export * from './notification';
export * from './ipc-response';
@@ -5,6 +6,10 @@ export * from './app-start-info';
export * from './configuration-reply';
export * from './version-information';
export * from './device-connection-state';
export * from './left-module-info';
export * from './hardware-modules';
export * from './hardware-module-info';
export * from './right-module-info';
export * from './save-user-configuration-data';
export * from './udev-rules-info';
export * from './update-firmware-data';
export * from './halves-info';

View File

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

View File

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

View File

@@ -0,0 +1,16 @@
/**
* What is the state of the udev rules.
* Only on Linux need extra udev rules.
*/
export enum UdevRulesInfo {
Unknown = 'Unknown',
Ok = 'Ok',
/**
* Udev rules not exists need to setup on Linux
*/
NeedToSetup = 'NeedToSetup',
/**
* Udev rules exist but different than expected on Linux
*/
Different = 'Different'
}

View File

@@ -0,0 +1,6 @@
import { VersionInformation } from './version-information';
export interface UpdateFirmwareData {
versionInformation: VersionInformation;
firmware?: Array<number>;
}

View File

@@ -1,4 +1,6 @@
export namespace Constants {
export const AGENT_GITHUB_URL = 'https://github.com/UltimateHackingKeyboard/agent';
export const AGENT_CONTRIBUTORS_GITHUB_PAGE_URL = 'https://github.com/UltimateHackingKeyboard/agent/graphs/contributors';
export const AGENT_CONTRIBUTORS_GITHUB_API_URL = 'https://api.github.com/repos/UltimateHackingKeyboard/agent/contributors';
export const FIRMWARE_GITHUB_ISSUE_URL = 'https://github.com/UltimateHackingKeyboard/agent/issues/new';
}

View File

@@ -15,28 +15,6 @@ export function capitalizeFirstLetter(text: string): string {
return text.charAt(0).toUpperCase() + text.slice(1);
}
/**
* This function coerces a string into a string literal type.
* Using tagged union types in TypeScript 2.0, this enables
* powerful typechecking of our reducers.
*
* Since every action label passes through this function it
* is a good place to ensure all of our action labels
* are unique.
*/
const typeCache: { [label: string]: boolean } = {};
export function type<T>(label: T | ''): T {
if (typeCache[<string>label]) {
throw new Error(`Action type "${label}" is not unique"`);
}
typeCache[<string>label] = true;
return <T>label;
}
export function runInElectron() {
return window && (<any>window).process && (<any>window).process.type;
}

View File

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

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