Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02044ae1d0 | ||
|
|
3f99d47bba | ||
|
|
9beadb4aac | ||
|
|
d9fb7a4b42 | ||
|
|
83912ec21f | ||
|
|
6c7232a5ba | ||
|
|
65fc8b5efb | ||
|
|
7a64191955 | ||
|
|
1a413c824e | ||
|
|
e545c9d67b | ||
|
|
8650fef7ae | ||
|
|
5c618869a2 | ||
|
|
1b8d6949e0 | ||
|
|
aabc0a8746 | ||
|
|
9589398834 | ||
|
|
933a715ea5 | ||
|
|
df14e2d569 | ||
|
|
4f8a0247d3 | ||
|
|
85ec5f6b6a | ||
|
|
739b830f47 | ||
|
|
482cff3d3b | ||
|
|
9284ae5032 | ||
|
|
cac6fdc190 | ||
|
|
ca9bf60a1b | ||
|
|
bb7edb8e4d | ||
|
|
0d9c976eb8 | ||
|
|
288d4f75b6 | ||
|
|
73e07eae2d | ||
|
|
8e620caac5 | ||
|
|
0d4e1acf76 | ||
|
|
88c42d58b1 | ||
|
|
5099e904fc | ||
|
|
5476f7c3a5 | ||
|
|
e0bb0bcca3 | ||
|
|
124c3ec29b | ||
|
|
2310320b8a | ||
|
|
67346b4cda | ||
|
|
662ca0152f | ||
|
|
6358528438 | ||
|
|
02f1053d46 | ||
|
|
a44a7dc5f8 | ||
|
|
02d57fdabf | ||
|
|
38f6688930 | ||
|
|
6ca12d0ccd | ||
|
|
acd17ac657 |
@@ -45,7 +45,6 @@ addons:
|
||||
|
||||
install:
|
||||
- nvm install
|
||||
- npm i -g npm@5.6.0
|
||||
- npm install
|
||||
|
||||
before_script:
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
|
||||
|
||||
## [1.1.1] - 2017-02-13
|
||||
|
||||
Firmware: 8.1.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.2)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- Sign Agent on OSX resulting in easier installation.
|
||||
- Add per-keymap description field.
|
||||
- Sort keymaps and macros alphabetically within the key action popover.
|
||||
- Add tooltip regarding non-US scancodes.
|
||||
- When deleting a macro, also delete the relevant play macro actions.
|
||||
- Make the reset configuration button persist the reset configuration in Agent-web.
|
||||
- Make Agent able to unbrick bricked modules.
|
||||
- Assign "switch to test keymap" action on all keymaps in the default configuration.
|
||||
- Add keymap descriptions in the default configuration.
|
||||
|
||||
## [1.1.0] - 2017-01-15
|
||||
|
||||
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
@@ -18,7 +18,6 @@ shallow_clone: true
|
||||
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- npm i -g npm@5.6.0
|
||||
- choco install chromium
|
||||
- set CI=true
|
||||
- set PATH=%APPDATA%\npm;%PATH%
|
||||
|
||||
375
package-lock.json
generated
375
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,8 @@
|
||||
"private": true,
|
||||
"author": "Ultimate Gadget Laboratories",
|
||||
"main": "electron/dist/electron-main.js",
|
||||
"version": "1.1.0",
|
||||
"firmwareVersion": "8.1.0",
|
||||
"version": "1.1.1",
|
||||
"firmwareVersion": "8.1.2",
|
||||
"deviceProtocolVersion": "4.2.0",
|
||||
"userConfigVersion": "4.0.0",
|
||||
"hardwareConfigVersion": "1.0.0",
|
||||
@@ -36,7 +36,7 @@
|
||||
"decompress": "4.2.0",
|
||||
"decompress-tarbz2": "^4.1.1",
|
||||
"devtron": "1.4.0",
|
||||
"electron": "1.7.5",
|
||||
"electron": "1.7.11",
|
||||
"electron-builder": "19.45.5",
|
||||
"electron-debug": "1.4.0",
|
||||
"electron-devtools-installer": "2.2.0",
|
||||
|
||||
@@ -7,6 +7,7 @@ import { SwitchLayerAction } from './switch-layer-action';
|
||||
import { SwitchKeymapAction, UnresolvedSwitchKeymapAction } from './switch-keymap-action';
|
||||
import { MouseAction } from './mouse-action';
|
||||
import { PlayMacroAction } from './play-macro-action';
|
||||
import { NoneAction } from './none-action';
|
||||
|
||||
export class Helper {
|
||||
|
||||
@@ -77,6 +78,8 @@ export class Helper {
|
||||
return new MouseAction().fromJsonObject(keyAction);
|
||||
case keyActionType.PlayMacroAction:
|
||||
return new PlayMacroAction().fromJsonObject(keyAction, macros);
|
||||
case keyActionType.NoneAction:
|
||||
return new NoneAction();
|
||||
default:
|
||||
throw `Invalid KeyAction.keyActionType: "${keyAction.keyActionType}"`;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ export enum ConfigBufferId {
|
||||
export enum DevicePropertyIds {
|
||||
DeviceProtocolVersion = 0,
|
||||
ProtocolVersions = 1,
|
||||
ConfigSizes = 2
|
||||
ConfigSizes = 2,
|
||||
CurrentKbootCommand = 3
|
||||
}
|
||||
|
||||
export enum EnumerationModes {
|
||||
|
||||
@@ -5,8 +5,13 @@ import * as fs from 'fs';
|
||||
import { UhkBlhost } from './uhk-blhost';
|
||||
import { UhkHidDevice } from './uhk-hid-device';
|
||||
import { snooze } from './util';
|
||||
import { convertBufferToIntArray, getTransferBuffers, DevicePropertyIds, UsbCommand, ConfigBufferId
|
||||
} from '../index';
|
||||
import {
|
||||
convertBufferToIntArray,
|
||||
getTransferBuffers,
|
||||
DevicePropertyIds,
|
||||
UsbCommand,
|
||||
ConfigBufferId
|
||||
} from '../index';
|
||||
import { LoadConfigurationsResult } from './models/load-configurations-result';
|
||||
|
||||
export class UhkOperations {
|
||||
@@ -42,6 +47,13 @@ export class UhkOperations {
|
||||
await snooze(1000);
|
||||
await this.device.jumpToBootloaderModule(ModuleSlotToId.leftHalf);
|
||||
this.device.close();
|
||||
|
||||
const leftModuleBricked = await this.waitForKbootIdle();
|
||||
if (!leftModuleBricked) {
|
||||
this.logService.error('[UhkOperations] Couldn\'t connect to the left keyboard half.');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.device.reenumerate(EnumerationModes.Buspal);
|
||||
this.device.close();
|
||||
await this.blhost.runBlhostCommandRetry([...buspalPrefix, 'get-property', '1']);
|
||||
@@ -161,6 +173,26 @@ export class UhkOperations {
|
||||
}
|
||||
}
|
||||
|
||||
public async waitForKbootIdle(): Promise<boolean> {
|
||||
const timeoutTime = new Date(new Date().getTime() + 30000);
|
||||
|
||||
while (new Date() < timeoutTime) {
|
||||
const buffer = await this.device.write(new Buffer([UsbCommand.GetProperty, DevicePropertyIds.CurrentKbootCommand]));
|
||||
this.device.close();
|
||||
|
||||
if (buffer[1] === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: max-line-length
|
||||
this.logService.info('[DeviceOperation] Cannot ping the bootloader. Please reconnect the left keyboard half. It probably needs several tries, so keep reconnecting until you see this message.');
|
||||
|
||||
await snooze(1000);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* IpcMain handler. Send the UserConfiguration to the UHK Device and send a response with the result.
|
||||
* @param {string} json - UserConfiguration in JSON format
|
||||
|
||||
2737
packages/uhk-web/package-lock.json
generated
2737
packages/uhk-web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,7 @@
|
||||
"@types/jasmine": "2.5.53",
|
||||
"@types/jasminewd2": "2.0.2",
|
||||
"@types/jquery": "3.2.9",
|
||||
"@types/lodash-es": "4.17.0",
|
||||
"@types/node-hid": "0.5.2",
|
||||
"@types/usb": "1.1.3",
|
||||
"angular-confirmation-popover": "3.2.0",
|
||||
@@ -67,7 +68,7 @@
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-jasmine-html-reporter": "0.2.2",
|
||||
"less-loader": "4.0.5",
|
||||
"lodash": "4.17.4",
|
||||
"lodash-es": "4.17.4",
|
||||
"ng2-dragula": "1.5.0",
|
||||
"ng2-nouislider": "^1.7.6",
|
||||
"ng2-select2": "1.0.0-beta.10",
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<div class="text-center">
|
||||
<span *ngIf="showPlaceholder"
|
||||
class="placeholder">
|
||||
<span (click)="editText()">{{ placeholder }}</span>
|
||||
</span>
|
||||
|
||||
<span *ngIf="showText"
|
||||
class="editable">
|
||||
<span (click)="editText()"
|
||||
[innerHtml]="displayText"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="editing">
|
||||
<textarea class="text-editor"
|
||||
[(ngModel)]="text"
|
||||
autofocus
|
||||
(keydown.control.enter)="keydownEnter()"
|
||||
(keydown.alt.enter)="keydownEnter()"></textarea>
|
||||
<div class="pull-right buttons">
|
||||
<button class="btn btn-danger"
|
||||
(click)="cancelEditText()">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
(click)="saveText()"
|
||||
[disabled]="isSaveDisabled">
|
||||
Update description
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
:host {
|
||||
margin-top: 0.5em;
|
||||
|
||||
span.placeholder {
|
||||
color: gray;
|
||||
display: inline-block;
|
||||
|
||||
.glyphicon {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
span.editable,
|
||||
span.placeholder {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
textarea.text-editor {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'editable-text',
|
||||
templateUrl: './editable-text.component.html',
|
||||
styleUrls: ['./editable-text.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableTextComponent), multi: true}
|
||||
]
|
||||
})
|
||||
export class EditableTextComponent implements ControlValueAccessor {
|
||||
|
||||
@Input() placeholder = 'No editable content';
|
||||
text: string;
|
||||
originalText: string;
|
||||
editing = false;
|
||||
|
||||
get isSaveDisabled(): boolean {
|
||||
return !this.text || this.text.trim().length === 0;
|
||||
}
|
||||
|
||||
get displayText(): string {
|
||||
return this.text && this.text.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {
|
||||
|
||||
}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
if (this.text === obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.text = obj;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.textChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
}
|
||||
|
||||
saveText(): void {
|
||||
this.originalText = null;
|
||||
this.editing = false;
|
||||
this.textChange(this.text);
|
||||
}
|
||||
|
||||
editText(): void {
|
||||
this.originalText = this.text;
|
||||
this.editing = true;
|
||||
}
|
||||
|
||||
cancelEditText(): void {
|
||||
this.text = this.originalText;
|
||||
this.editing = false;
|
||||
}
|
||||
|
||||
keydownEnter(): void {
|
||||
if (this.isSaveDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.saveText();
|
||||
}
|
||||
|
||||
get showPlaceholder(): boolean {
|
||||
return !this.editing && !this.text;
|
||||
}
|
||||
|
||||
get showText(): boolean {
|
||||
return !this.editing && !!this.text;
|
||||
}
|
||||
|
||||
private textChange: any = () => {
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,11 @@
|
||||
[selectedKey]="selectedKey"
|
||||
[selected]="selectedKey?.layerId === index"
|
||||
[keyboardLayout]="keyboardLayout"
|
||||
[description]="description"
|
||||
[showDescription]="true"
|
||||
(keyClick)="keyClick.emit($event)"
|
||||
(keyHover)="keyHover.emit($event)"
|
||||
(capture)="capture.emit($event)"
|
||||
(descriptionChanged)="descriptionChanged.emit($event)"
|
||||
>
|
||||
</svg-keyboard>
|
||||
|
||||
|
Before Width: | Height: | Size: 659 B After Width: | Height: | Size: 809 B |
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
|
||||
import { Layer } from 'uhk-common';
|
||||
|
||||
@@ -81,11 +81,14 @@ export class KeyboardSliderComponent implements OnChanges {
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() selectedKey: { layerId: number, moduleId: number, keyId: number };
|
||||
@Input() keyboardLayout = KeyboardLayout.ANSI;
|
||||
@Input() description: string;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
@Output() descriptionChanged = new EventEmitter<string>();
|
||||
|
||||
layerAnimationState: AnimationKeyboard[];
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['layers']) {
|
||||
this.layerAnimationState = this.layers.map<AnimationKeyboard>(() => 'initOut');
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
(downloadClick)="downloadKeymap()"></keymap-header>
|
||||
<svg-keyboard-wrap [keymap]="keymap$ | async"
|
||||
[halvesSplit]="keyboardSplit"
|
||||
[keyboardLayout]="keyboardLayout$ | async"></svg-keyboard-wrap>
|
||||
[keyboardLayout]="keyboardLayout$ | async"
|
||||
(descriptionChanged)="descriptionChanged($event)"></svg-keyboard-wrap>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="!(keymap$ | async)" class="not-found">
|
||||
|
||||
@@ -18,6 +18,8 @@ import { AppState, getKeyboardLayout } from '../../../store';
|
||||
import { getKeymap, getKeymaps, getUserConfiguration } from '../../../store/reducers/user-configuration';
|
||||
import { SvgKeyboardWrapComponent } from '../../svg/wrap';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
|
||||
|
||||
@Component({
|
||||
selector: 'keymap-edit',
|
||||
@@ -64,7 +66,7 @@ export class KeymapEditComponent {
|
||||
const keymap = latest[0];
|
||||
const exportableJSON = latest[1];
|
||||
const fileName = keymap.name + '_keymap.json';
|
||||
saveAs(new Blob([exportableJSON], { type: 'application/json' }), fileName);
|
||||
saveAs(new Blob([exportableJSON], {type: 'application/json'}), fileName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,6 +75,10 @@ export class KeymapEditComponent {
|
||||
this.keyboardSplit = !this.keyboardSplit;
|
||||
}
|
||||
|
||||
descriptionChanged(event: ChangeKeymapDescription): void {
|
||||
this.store.dispatch(new KeymapActions.EditDescriptionAction(event));
|
||||
}
|
||||
|
||||
private toExportableJSON(keymap: Keymap): Observable<any> {
|
||||
return this.store
|
||||
.let(getUserConfiguration())
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="!macro" class="not-found">
|
||||
There is no macro with id {{ route.params.select('id') | async }}.
|
||||
There is no macro with id {{ macroId }}.
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Subscription } from 'rxjs/Subscription';
|
||||
import 'rxjs/add/operator/pluck';
|
||||
|
||||
import { MacroActions } from '../../../store/actions';
|
||||
import { AppState } from '../../../store/index';
|
||||
import { AppState } from '../../../store';
|
||||
import { getMacro } from '../../../store/reducers/user-configuration';
|
||||
|
||||
@Component({
|
||||
@@ -21,13 +21,17 @@ import { getMacro } from '../../../store/reducers/user-configuration';
|
||||
export class MacroEditComponent implements OnDestroy {
|
||||
macro: Macro;
|
||||
isNew: boolean;
|
||||
macroId: number;
|
||||
|
||||
private subscription: Subscription;
|
||||
constructor(private store: Store<AppState>, public route: ActivatedRoute) {
|
||||
this.subscription = route
|
||||
.params
|
||||
.pluck<{}, string>('id')
|
||||
.switchMap((id: string) => store.let(getMacro(+id)))
|
||||
.switchMap((id: string) => {
|
||||
this.macroId = +id;
|
||||
return store.let(getMacro(this.macroId));
|
||||
})
|
||||
.subscribe((macro: Macro) => {
|
||||
this.macro = macro;
|
||||
});
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
[width]="200"
|
||||
[options]="options"
|
||||
></select2>
|
||||
<icon name="question-circle"
|
||||
data-toggle="tooltip"
|
||||
title="Looking for a non-US character, but can't find it? Please note that USB keyboards send scancodes, not characters to your computer. Then your operating system translates the scancodes to characters according to your current OS keyboard layout. This means that you have to select the US-equivalent character of the desired key in Agent."
|
||||
data-placement="bottom"></icon>
|
||||
<capture-keystroke-button (capture)="onKeysCapture($event)" tabindex="0"></capture-keystroke-button>
|
||||
</div>
|
||||
<div class="modifier-options">
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
icon {
|
||||
display: inline-block;
|
||||
};
|
||||
}
|
||||
|
||||
.modifier-options {
|
||||
|
||||
@@ -164,12 +164,12 @@ ul {
|
||||
padding: 0;
|
||||
margin: 0 0.25rem;
|
||||
text-overflow: ellipsis;
|
||||
background-color: inherit;
|
||||
background-color: transparent;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
|
||||
border-color: transparent;
|
||||
background-color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,17 +56,8 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
|
||||
addon: 'active'
|
||||
};
|
||||
|
||||
this.keymaps$ = store.let(getKeymaps())
|
||||
.map(keymaps => keymaps.slice()) // Creating a new array reference, because the sort is working in place
|
||||
.do((keymaps: Keymap[]) => {
|
||||
keymaps.sort((first: Keymap, second: Keymap) => first.name.localeCompare(second.name));
|
||||
});
|
||||
|
||||
this.macros$ = store.let(getMacros())
|
||||
.map(macros => macros.slice()) // Creating a new array reference, because the sort is working in place
|
||||
.do((macros: Macro[]) => {
|
||||
macros.sort((first: Macro, second: Macro) => first.name.localeCompare(second.name));
|
||||
});
|
||||
this.keymaps$ = store.let(getKeymaps());
|
||||
this.macros$ = store.let(getMacros());
|
||||
|
||||
this.showAddonMenu$ = this.store.select(showAddonMenu);
|
||||
this.runInElectron$ = this.store.select(runningInElectron);
|
||||
|
||||
@@ -14,3 +14,7 @@
|
||||
(capture)="onCapture(i, $event.index, $event.captured)"
|
||||
/>
|
||||
</svg>
|
||||
<editable-text *ngIf="showDescription"
|
||||
[ngModel]="description"
|
||||
(ngModelChange)="descriptionChanged.emit($event)"
|
||||
placeholder="No description provided for this keymap."></editable-text>
|
||||
|
||||
|
Before Width: | Height: | Size: 854 B After Width: | Height: | Size: 1.0 KiB |
@@ -1,5 +1,10 @@
|
||||
:host {
|
||||
display: flex;
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
editable-text {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,12 @@ export class SvgKeyboardComponent implements OnInit {
|
||||
@Input() selected: boolean;
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() keyboardLayout = KeyboardLayout.ANSI;
|
||||
@Input() description: string;
|
||||
@Input() showDescription = false;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
@Output() descriptionChanged = new EventEmitter<string>();
|
||||
|
||||
modules: SvgModule[];
|
||||
viewBox: string;
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
[selectedKey]="selectedKey"
|
||||
[halvesSplit]="halvesSplit"
|
||||
[keyboardLayout]="keyboardLayout"
|
||||
[description]="keymap.description"
|
||||
(keyClick)="onKeyClick($event.moduleId, $event.keyId, $event.keyTarget)"
|
||||
(keyHover)="onKeyHover($event.moduleId, $event.event, $event.over, $event.keyId)"
|
||||
(capture)="onCapture($event.moduleId, $event.keyId, $event.captured)"
|
||||
(descriptionChanged)="onDescriptionChanged($event)"
|
||||
></keyboard-slider>
|
||||
<popover tabindex="0" [visible]="popoverShown" [keyPosition]="keyPosition" [wrapPosition]="wrapPosition" [defaultKeyAction]="popoverInitKeyAction"
|
||||
[currentKeymap]="keymap" [currentLayer]="currentLayer" (cancel)="hidePopover()" (remap)="onRemap($event)"></popover>
|
||||
|
||||
@@ -2,14 +2,16 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Renderer,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
SimpleChanges
|
||||
Output,
|
||||
Renderer,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
@@ -25,10 +27,10 @@ import {
|
||||
KeystrokeAction,
|
||||
Layer,
|
||||
LayerName,
|
||||
SecondaryRoleAction,
|
||||
MouseAction,
|
||||
MouseActionParam,
|
||||
PlayMacroAction,
|
||||
SecondaryRoleAction,
|
||||
SwitchKeymapAction,
|
||||
SwitchLayerAction
|
||||
} from 'uhk-common';
|
||||
@@ -38,6 +40,7 @@ import { AppState } from '../../../store';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
import { PopoverComponent } from '../../popover';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
|
||||
|
||||
interface NameValuePair {
|
||||
name: string;
|
||||
@@ -56,6 +59,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
|
||||
@Input() tooltipEnabled: boolean = false;
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() keyboardLayout: KeyboardLayout.ANSI;
|
||||
@Output() descriptionChanged = new EventEmitter<ChangeKeymapDescription>();
|
||||
|
||||
@ViewChild(PopoverComponent, { read: ElementRef }) popover: ElementRef;
|
||||
|
||||
@@ -237,6 +241,13 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
|
||||
return this.currentLayer;
|
||||
}
|
||||
|
||||
onDescriptionChanged(description: string): void {
|
||||
this.descriptionChanged.emit({
|
||||
description,
|
||||
abbr: this.keymap.abbreviation
|
||||
});
|
||||
}
|
||||
|
||||
private getKeyActionContent(keyAction: KeyAction): Observable<NameValuePair[]> {
|
||||
if (keyAction instanceof KeystrokeAction) {
|
||||
const keystrokeAction: KeystrokeAction = keyAction;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { Notification } from 'uhk-common';
|
||||
import { AppState, getUndoableNotification } from '../../store/index';
|
||||
import { AppState, getUndoableNotification } from '../../store';
|
||||
import { DismissUndoNotificationAction, UndoLastAction } from '../../store/actions/app';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { AfterViewInit, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[autofocus]'
|
||||
})
|
||||
export class Autofocus implements AfterViewInit {
|
||||
constructor(private el: ElementRef) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.el.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ChangeKeymapDescription {
|
||||
abbr: string;
|
||||
description: string;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -101,6 +101,8 @@ import { UhkDeviceLoadingGuard } from './services/uhk-device-loading.guard';
|
||||
import { UhkDeviceLoadedGuard } from './services/uhk-device-loaded.guard';
|
||||
import { XtermComponent } from './components/xterm/xterm.component';
|
||||
import { SliderWrapperComponent } from './components/slider-wrapper/slider-wrapper.component';
|
||||
import { EditableTextComponent } from './components/editable-text/editable-text.component';
|
||||
import { Autofocus } from './directives/autofocus/autofocus.directive';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -169,7 +171,9 @@ import { SliderWrapperComponent } from './components/slider-wrapper/slider-wrapp
|
||||
ProgressButtonComponent,
|
||||
LoadingDevicePageComponent,
|
||||
XtermComponent,
|
||||
SliderWrapperComponent
|
||||
SliderWrapperComponent,
|
||||
EditableTextComponent,
|
||||
Autofocus
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { KeyAction, Keymap, Macro } from 'uhk-common';
|
||||
import { UndoUserConfigData } from '../../models/undo-user-config-data';
|
||||
import { ChangeKeymapDescription } from '../../models/ChangeKeymapDescription';
|
||||
|
||||
export type KeymapAction =
|
||||
KeymapActions.AddKeymapAction |
|
||||
@@ -11,7 +12,8 @@ export type KeymapAction =
|
||||
KeymapActions.SetDefaultAction |
|
||||
KeymapActions.RemoveKeymapAction |
|
||||
KeymapActions.SaveKeyAction |
|
||||
KeymapActions.CheckMacroAction;
|
||||
KeymapActions.CheckMacroAction |
|
||||
KeymapActions.EditDescriptionAction;
|
||||
|
||||
export namespace KeymapActions {
|
||||
export const ADD = '[Keymap] Add keymap';
|
||||
@@ -98,6 +100,16 @@ export namespace KeymapActions {
|
||||
payload: UndoUserConfigData
|
||||
};
|
||||
|
||||
export const EDIT_DESCRIPTION = '[Keymap] Edit description';
|
||||
|
||||
export class EditDescriptionAction {
|
||||
type = EDIT_DESCRIPTION;
|
||||
|
||||
constructor(public payload: ChangeKeymapDescription) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export function loadKeymaps(): Action {
|
||||
return {
|
||||
type: KeymapActions.LOAD_KEYMAPS
|
||||
|
||||
@@ -35,10 +35,12 @@ import { ShowNotificationAction } from '../actions/app';
|
||||
import { AppState } from '../index';
|
||||
import {
|
||||
ActionTypes as UserConfigActions,
|
||||
ApplyUserConfigurationFromFileAction,
|
||||
LoadConfigFromDeviceAction,
|
||||
LoadResetUserConfigurationAction
|
||||
} from '../actions/user-config';
|
||||
import { DefaultUserConfigurationService } from '../../services/default-user-configuration.service';
|
||||
import { DataStorageRepositoryService } from '../../services/datastorage-repository.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeviceEffects {
|
||||
@@ -162,8 +164,16 @@ export class DeviceEffects {
|
||||
});
|
||||
|
||||
@Effect() saveResetUserConfigurationToDevice$ = this.actions$
|
||||
.ofType(UserConfigActions.LOAD_RESET_USER_CONFIGURATION, UserConfigActions.APPLY_USER_CONFIGURATION_FROM_FILE)
|
||||
.switchMap(() => Observable.of(new SaveConfigurationAction()));
|
||||
.ofType<ApplyUserConfigurationFromFileAction
|
||||
| LoadResetUserConfigurationAction>(
|
||||
UserConfigActions.LOAD_RESET_USER_CONFIGURATION,
|
||||
UserConfigActions.APPLY_USER_CONFIGURATION_FROM_FILE)
|
||||
.map(action => action.payload)
|
||||
.switchMap((config: UserConfiguration) => {
|
||||
this.dataStorageRepository.saveConfig(config);
|
||||
|
||||
return Observable.of(new SaveConfigurationAction());
|
||||
});
|
||||
|
||||
@Effect({dispatch: false}) updateFirmware$ = this.actions$
|
||||
.ofType<UpdateFirmwareAction>(ActionTypes.UPDATE_FIRMWARE)
|
||||
@@ -193,6 +203,7 @@ export class DeviceEffects {
|
||||
private router: Router,
|
||||
private deviceRendererService: DeviceRendererService,
|
||||
private store: Store<AppState>,
|
||||
private dataStorageRepository: DataStorageRepositoryService,
|
||||
private defaultUserConfigurationService: DefaultUserConfigurationService) {
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,17 @@ import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/pairwise';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import 'rxjs/add/operator/withLatestFrom';
|
||||
import 'rxjs/add/observable/of';
|
||||
|
||||
import { Keymap } from 'uhk-common';
|
||||
import { findNewItem } from '../../util';
|
||||
import { KeymapActions } from '../actions';
|
||||
import { AppState } from '../index';
|
||||
import { getKeymaps } from '../reducers/user-configuration';
|
||||
|
||||
@Injectable()
|
||||
export class KeymapEffects {
|
||||
@@ -32,10 +35,10 @@ export class KeymapEffects {
|
||||
|
||||
@Effect({ dispatch: false }) addOrDuplicate$: any = this.actions$
|
||||
.ofType(KeymapActions.ADD, KeymapActions.DUPLICATE)
|
||||
.withLatestFrom(this.store)
|
||||
.map(latest => latest[1].userConfiguration.keymaps)
|
||||
.do(keymaps => {
|
||||
this.router.navigate(['/keymap', keymaps[keymaps.length - 1].abbreviation]);
|
||||
.withLatestFrom(this.store.let(getKeymaps()).pairwise(), (action, latest) => latest)
|
||||
.do(([prevKeymaps, newKeymaps]) => {
|
||||
const newKeymap = findNewItem(prevKeymaps, newKeymaps);
|
||||
this.router.navigate(['/keymap', newKeymap.abbreviation]);
|
||||
});
|
||||
|
||||
@Effect({ dispatch: false }) remove$: any = this.actions$
|
||||
|
||||
@@ -2,14 +2,18 @@ import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Store, Action } from '@ngrx/store';
|
||||
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/pairwise';
|
||||
import 'rxjs/add/operator/withLatestFrom';
|
||||
|
||||
import { Macro } from 'uhk-common';
|
||||
import { KeymapActions, MacroActions } from '../actions';
|
||||
import { AppState } from '../index';
|
||||
import { getMacros } from '../reducers/user-configuration';
|
||||
import { findNewItem } from '../../util';
|
||||
|
||||
@Injectable()
|
||||
export class MacroEffects {
|
||||
@@ -27,23 +31,17 @@ export class MacroEffects {
|
||||
}
|
||||
});
|
||||
|
||||
@Effect({ dispatch: false }) add$: any = this.actions$
|
||||
.ofType(MacroActions.ADD)
|
||||
.withLatestFrom(this.store)
|
||||
.map(([action, state]) => state.userConfiguration.macros)
|
||||
.map(macros => macros[macros.length - 1])
|
||||
.do(lastMacro => {
|
||||
this.router.navigate(['/macro', lastMacro.id, 'new']);
|
||||
@Effect({ dispatch: false }) addOrDuplicate$: any = this.actions$
|
||||
.ofType(MacroActions.ADD, MacroActions.DUPLICATE)
|
||||
.withLatestFrom(this.store.let(getMacros()).pairwise(), (action, latest) => ([action, latest[0], latest[1]]))
|
||||
.do(([action, prevMacros, newMacros]: [Action, Macro[], Macro[]]) => {
|
||||
const newMacro = findNewItem(prevMacros, newMacros);
|
||||
const commands = ['/macro', newMacro.id];
|
||||
if (action.type === MacroActions.ADD) {
|
||||
commands.push('new');
|
||||
}
|
||||
this.router.navigate(commands);
|
||||
});
|
||||
|
||||
@Effect({ dispatch: false }) duplicate: any = this.actions$
|
||||
.ofType(MacroActions.DUPLICATE)
|
||||
.withLatestFrom(this.store)
|
||||
.map(([action, state]) => state.userConfiguration.macros)
|
||||
.map(macros => macros[macros.length - 1])
|
||||
.do(lastMacro => {
|
||||
this.router.navigate(['/macro', lastMacro.id]);
|
||||
});
|
||||
|
||||
constructor(private actions$: Actions, private router: Router, private store: Store<AppState>) {}
|
||||
constructor(private actions$: Actions, private router: Router, private store: Store<AppState>) { }
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ export class UserConfigEffects {
|
||||
@Effect() saveUserConfig$: Observable<Action> = (this.actions$
|
||||
.ofType(
|
||||
KeymapActions.ADD, KeymapActions.DUPLICATE, KeymapActions.EDIT_NAME, KeymapActions.EDIT_ABBR,
|
||||
KeymapActions.SET_DEFAULT, KeymapActions.REMOVE, KeymapActions.SAVE_KEY,
|
||||
KeymapActions.SET_DEFAULT, KeymapActions.REMOVE, KeymapActions.SAVE_KEY, KeymapActions.EDIT_DESCRIPTION,
|
||||
MacroActions.ADD, MacroActions.DUPLICATE, MacroActions.EDIT_NAME, MacroActions.REMOVE, MacroActions.ADD_ACTION,
|
||||
MacroActions.SAVE_ACTION, MacroActions.DELETE_ACTION, MacroActions.REORDER_ACTION,
|
||||
ActionTypes.RENAME_USER_CONFIGURATION, ActionTypes.SET_USER_CONFIGURATION_VALUE) as
|
||||
|
||||
@@ -7,7 +7,7 @@ export const initialState: Keymap[] = [];
|
||||
export function reducer(state = initialState, action: KeymapAction): Keymap[] {
|
||||
switch (action.type) {
|
||||
case KeymapActions.LOAD_KEYMAPS_SUCCESS: {
|
||||
return action.payload;
|
||||
return (action as KeymapActions.LoadKeymapSuccessAction).payload ;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -4,7 +4,18 @@ import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { KeyAction, KeyActionHelper, Keymap, Layer, Macro, Module, SwitchLayerAction, UserConfiguration } from 'uhk-common';
|
||||
import {
|
||||
KeyAction,
|
||||
KeyActionHelper,
|
||||
Keymap,
|
||||
Layer,
|
||||
Macro,
|
||||
Module,
|
||||
NoneAction,
|
||||
PlayMacroAction,
|
||||
SwitchLayerAction,
|
||||
UserConfiguration
|
||||
} from 'uhk-common';
|
||||
import { KeymapActions, MacroActions } from '../actions';
|
||||
import { AppState } from '../index';
|
||||
import { ActionTypes } from '../actions/user-config';
|
||||
@@ -19,7 +30,12 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
case ActionTypes.APPLY_USER_CONFIGURATION_FROM_FILE:
|
||||
case ActionTypes.LOAD_RESET_USER_CONFIGURATION:
|
||||
case ActionTypes.LOAD_USER_CONFIG_SUCCESS: {
|
||||
return Object.assign(changedUserConfiguration, action.payload);
|
||||
Object.assign(changedUserConfiguration, action.payload);
|
||||
changedUserConfiguration.keymaps = [...changedUserConfiguration.keymaps];
|
||||
changedUserConfiguration.keymaps.sort((first: Keymap, second: Keymap) => first.name.localeCompare(second.name));
|
||||
changedUserConfiguration.macros = [...changedUserConfiguration.macros];
|
||||
changedUserConfiguration.macros.sort((first: Macro, second: Macro) => first.name.localeCompare(second.name));
|
||||
return changedUserConfiguration;
|
||||
}
|
||||
|
||||
case KeymapActions.ADD:
|
||||
@@ -29,7 +45,7 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
newKeymap.name = generateName(state.keymaps, newKeymap.name);
|
||||
newKeymap.isDefault = (state.keymaps.length === 0);
|
||||
|
||||
changedUserConfiguration.keymaps = state.keymaps.concat(newKeymap);
|
||||
changedUserConfiguration.keymaps = insertItemInNameOrder(state.keymaps, newKeymap);
|
||||
break;
|
||||
}
|
||||
case KeymapActions.EDIT_NAME: {
|
||||
@@ -38,20 +54,27 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
}
|
||||
|
||||
const name: string = action.payload.name.trim();
|
||||
let keymapToRename: Keymap = null;
|
||||
|
||||
const duplicate = state.keymaps.some((keymap: Keymap) => {
|
||||
if (keymap.abbreviation === action.payload.abbr) {
|
||||
keymapToRename = keymap;
|
||||
}
|
||||
|
||||
return keymap.name === name && keymap.abbreviation !== action.payload.abbr;
|
||||
});
|
||||
|
||||
changedUserConfiguration.keymaps = state.keymaps.map((keymap: Keymap) => {
|
||||
keymap = Object.assign(new Keymap(), keymap);
|
||||
|
||||
if (!duplicate && keymap.abbreviation === action.payload.abbr) {
|
||||
keymap.name = name;
|
||||
if (duplicate) {
|
||||
break;
|
||||
}
|
||||
return keymap;
|
||||
});
|
||||
|
||||
const newKeymap = Object.assign(new Keymap(), keymapToRename, { name });
|
||||
|
||||
changedUserConfiguration.keymaps = insertItemInNameOrder(
|
||||
state.keymaps,
|
||||
newKeymap,
|
||||
keymap => keymap.abbreviation !== newKeymap.abbreviation
|
||||
);
|
||||
break;
|
||||
}
|
||||
case KeymapActions.EDIT_ABBR: {
|
||||
@@ -163,7 +186,7 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
newMacro.isPrivate = true;
|
||||
newMacro.macroActions = [];
|
||||
|
||||
changedUserConfiguration.macros = state.macros.concat(newMacro);
|
||||
changedUserConfiguration.macros = insertItemInNameOrder(state.macros, newMacro);
|
||||
break;
|
||||
}
|
||||
case MacroActions.DUPLICATE: {
|
||||
@@ -171,7 +194,7 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
newMacro.name = generateName(state.macros, newMacro.name);
|
||||
newMacro.id = generateMacroId(state.macros);
|
||||
|
||||
changedUserConfiguration.macros = state.macros.concat(newMacro);
|
||||
changedUserConfiguration.macros = insertItemInNameOrder(state.macros, newMacro);
|
||||
break;
|
||||
}
|
||||
case MacroActions.EDIT_NAME: {
|
||||
@@ -180,25 +203,52 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
}
|
||||
|
||||
const name: string = action.payload.name.trim();
|
||||
let macroToRename: Macro = null;
|
||||
|
||||
const duplicate = state.macros.some((macro: Macro) => {
|
||||
if (macro.id === action.payload.id) {
|
||||
macroToRename = macro;
|
||||
}
|
||||
|
||||
return macro.id !== action.payload.id && macro.name === name;
|
||||
});
|
||||
|
||||
changedUserConfiguration.macros = state.macros.map((macro: Macro) => {
|
||||
macro = Object.assign(new Macro(), macro);
|
||||
if (!duplicate && macro.id === action.payload.id) {
|
||||
macro.name = name;
|
||||
}
|
||||
|
||||
return macro;
|
||||
});
|
||||
|
||||
if (duplicate) {
|
||||
break;
|
||||
}
|
||||
|
||||
const newMacro = Object.assign(new Macro(), macroToRename, { name });
|
||||
changedUserConfiguration.macros = insertItemInNameOrder(state.macros, newMacro, macro => macro.id !== newMacro.id);
|
||||
break;
|
||||
}
|
||||
|
||||
case MacroActions.REMOVE:
|
||||
changedUserConfiguration.macros = state.macros.filter((macro: Macro) => macro.id !== action.payload);
|
||||
const macroId = action.payload;
|
||||
changedUserConfiguration.macros = state.macros.filter((macro: Macro) => macro.id !== macroId);
|
||||
|
||||
for (let k = 0; k < changedUserConfiguration.keymaps.length; k++) {
|
||||
const keymap = changedUserConfiguration.keymaps[k];
|
||||
let hasChanges = false;
|
||||
|
||||
for (const layer of keymap.layers) {
|
||||
for (const module of layer.modules) {
|
||||
for (let ka = 0; ka < module.keyActions.length; ka++) {
|
||||
const keyAction = module.keyActions[ka];
|
||||
|
||||
if (keyAction instanceof PlayMacroAction && keyAction.macroId === macroId) {
|
||||
hasChanges = true;
|
||||
module.keyActions[ka] = new NoneAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
changedUserConfiguration.keymaps[k] = new Keymap(keymap);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MacroActions.ADD_ACTION:
|
||||
changedUserConfiguration.macros = state.macros.map((macro: Macro) => {
|
||||
if (macro.id === action.payload.id) {
|
||||
@@ -263,6 +313,18 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
break;
|
||||
}
|
||||
|
||||
case KeymapActions.EDIT_DESCRIPTION: {
|
||||
const data = (action as KeymapActions.EditDescriptionAction).payload;
|
||||
|
||||
changedUserConfiguration.keymaps = state.keymaps.map(keymap => {
|
||||
if (keymap.abbreviation === data.abbr) {
|
||||
keymap.description = data.description;
|
||||
}
|
||||
return keymap;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -360,6 +422,27 @@ function generateMacroId(macros: Macro[]) {
|
||||
return newId + 1;
|
||||
}
|
||||
|
||||
function insertItemInNameOrder<T extends { name: string }>(
|
||||
items: T[], newItem: T, keepItem: (item: T) => boolean = () => true
|
||||
): T[] {
|
||||
const newItems: T[] = [];
|
||||
let added = false;
|
||||
for (const item of items) {
|
||||
if (!added && item.name.localeCompare(newItem.name) > 0) {
|
||||
newItems.push(newItem);
|
||||
added = true;
|
||||
}
|
||||
if (keepItem(item)) {
|
||||
newItems.push(item);
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
newItems.push(newItem);
|
||||
}
|
||||
|
||||
return newItems;
|
||||
}
|
||||
|
||||
function checkExistence(layers: Layer[], property: string, value: any): Layer[] {
|
||||
const keyActionsToClear: {
|
||||
layerIdx: number,
|
||||
|
||||
9
packages/uhk-web/src/app/util/find-new-item.ts
Normal file
9
packages/uhk-web/src/app/util/find-new-item.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function findNewItem<T>(oldItems: T[], newItems: T[]): T {
|
||||
for (let i = 0; i < oldItems.length; ++i) {
|
||||
if (oldItems[i] !== newItems[i]) {
|
||||
return newItems[i];
|
||||
}
|
||||
}
|
||||
|
||||
return newItems[newItems.length - 1];
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './find-new-item';
|
||||
export * from './html-helper';
|
||||
export * from './validators';
|
||||
export * from './version-helper';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cloneDeep } from 'lodash';
|
||||
import cloneDeep from 'lodash-es/cloneDeep';
|
||||
|
||||
const defaultUserConfig = {
|
||||
userConfigMajorVersion: 3,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require('path');
|
||||
const uhk = require('./uhk');
|
||||
const device = uhk.getUhkDevice();
|
||||
|
||||
let buffer = new Buffer(uhk.pushUint32([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.i2cBaudRate]));
|
||||
//console.log(buffer);
|
||||
device.write(uhk.getTransferData(buffer));
|
||||
let response = device.readSync();
|
||||
//console.log(Buffer.from(response));
|
||||
let requestedBaudRate = uhk.getUint32(response, 2);
|
||||
let actualBaudRate = uhk.getUint32(response, 6);
|
||||
console.log(`requestedBaudRate:${requestedBaudRate} | actualBaudRate:${actualBaudRate} | I2C0_F:0b${response[1].toString(2).padStart(8, '0')}`)
|
||||
(async function() {
|
||||
let response = await uhk.writeDevice(device, [uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.i2cBaudRate]);
|
||||
let requestedBaudRate = uhk.getUint32(response, 2);
|
||||
let actualBaudRate = uhk.getUint32(response, 6);
|
||||
let i2c0F = response[1].toString(2).padStart(8, '0');
|
||||
console.log(`requestedBaudRate:${requestedBaudRate} | actualBaudRate:${actualBaudRate} | I2C0_F:0b${i2c0F}`)
|
||||
})();
|
||||
|
||||
@@ -51,13 +51,13 @@ function convertMs(milliseconds) {
|
||||
|
||||
const device = uhk.getUhkDevice();
|
||||
|
||||
device.write(uhk.getTransferData(new Buffer(uhk.pushUint32([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.uptime]))));
|
||||
device.write(uhk.getTransferData(new Buffer([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.uptime])));
|
||||
let response = device.readSync();
|
||||
let uptimeMs = uhk.getUint32(response, 1);
|
||||
let uptime = convertMs(uptimeMs);
|
||||
console.log(`uptime: ${uptime.days}d ${String(uptime.hours).padStart(2, '0')}:${String(uptime.minutes).padStart(2, '0')}:${String(uptime.seconds).padStart(2, '0')}`)
|
||||
|
||||
device.write(uhk.getTransferData(new Buffer(uhk.pushUint32([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.i2cBaudRate]))));
|
||||
device.write(uhk.getTransferData(new Buffer([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.i2cBaudRate])));
|
||||
response = device.readSync();
|
||||
let requestedBaudRate = uhk.getUint32(response, 2);
|
||||
let actualBaudRate = uhk.getUint32(response, 6);
|
||||
|
||||
@@ -15,7 +15,7 @@ function convertMs(milliseconds) {
|
||||
return {days, hours, minutes, seconds};
|
||||
}
|
||||
|
||||
let buffer = new Buffer(uhk.pushUint32([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.uptime]));
|
||||
let buffer = new Buffer([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.uptime]);
|
||||
//console.log(buffer);
|
||||
device.write(uhk.getTransferData(buffer));
|
||||
let response = device.readSync();
|
||||
|
||||
@@ -9,6 +9,7 @@ program
|
||||
const moduleSlot = program.args[0];
|
||||
const moduleSlotId = uhk.checkModuleSlot(moduleSlot, uhk.moduleSlotToId);
|
||||
const device = uhk.getUhkDevice();
|
||||
let transfer = new Buffer([uhk.usbCommands.jumpToModuleBootloader, moduleSlotId]);
|
||||
device.write(uhk.getTransferData(transfer));
|
||||
const response = Buffer.from(device.readSync());
|
||||
|
||||
(async function() {
|
||||
await uhk.jumpToModuleBootloader(device, moduleSlotId);
|
||||
})();
|
||||
|
||||
@@ -1,52 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
const HID = require('node-hid');
|
||||
let uhk = require('./uhk');
|
||||
var program = require('commander');
|
||||
|
||||
program
|
||||
.option('-dt, --polling-timeout <n>', 'Polling timeout (ms)')
|
||||
.option('-bt, --bootloader-timeout <n>', 'Bootloader timeout (ms)')
|
||||
.option('-f, --force', 'Force reenumeration')
|
||||
.parse(process.argv);
|
||||
|
||||
let pollingTimeoutMs = 10000;
|
||||
const pollingIntervalMs = 100;
|
||||
const bootloaderTimeoutMs = 5000;
|
||||
let jumped = false;
|
||||
const uhk = require('./uhk');
|
||||
const program = require('commander');
|
||||
|
||||
program.parse(process.argv);
|
||||
const enumerationMode = program.args[0];
|
||||
const enumerationModeId = uhk.enumerationModes[enumerationMode];
|
||||
|
||||
if (enumerationModeId === undefined) {
|
||||
const enumerationModes = Object.keys(uhk.enumerationModes).join(', ');
|
||||
console.log(`Invalid enumeration mode '${enumerationMode}' is not one of: ${enumerationModes}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Trying to reenumerate as ${enumerationMode}...`);
|
||||
setInterval(() => {
|
||||
pollingTimeoutMs -= pollingIntervalMs;
|
||||
|
||||
const foundDevice = HID.devices().find(device =>
|
||||
device.vendorId === uhk.vendorId && device.productId === uhk.enumerationModeIdToProductId[enumerationModeId]);
|
||||
|
||||
if (foundDevice) {
|
||||
console.log(`${enumerationMode} is up`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (pollingTimeoutMs <= 0) {
|
||||
console.log(`Couldn't reenumerate as ${enumerationMode}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let device = uhk.getUhkDevice();
|
||||
if (device && !jumped) {
|
||||
console.log(`UHK found, reenumerating as ${enumerationMode}`);
|
||||
let t = bootloaderTimeoutMs;
|
||||
let message = new Buffer([uhk.usbCommands.reenumerate, enumerationModeId, t&0xff, (t&0xff<<8)>>8, (t&0xff<<16)>>16, (t&0xff<<24)>>24]);
|
||||
device.write(uhk.getTransferData(message));
|
||||
jumped = true;
|
||||
}
|
||||
|
||||
}, pollingIntervalMs);
|
||||
(async function() {
|
||||
await uhk.reenumerate(enumerationMode);
|
||||
})();
|
||||
|
||||
@@ -33,6 +33,6 @@ if (kbootCommand !== 'idle') {
|
||||
}
|
||||
|
||||
const device = uhk.getUhkDevice();
|
||||
let transfer = new Buffer([uhk.usbCommands.sendKbootCommandToModule, kbootCommandId, parseInt(i2cAddress)]);
|
||||
device.write(uhk.getTransferData(transfer));
|
||||
const response = Buffer.from(device.readSync());
|
||||
(async function() {
|
||||
await uhk.sendKbootCommandToModule(device, kbootCommandId, i2cAddress);
|
||||
})();
|
||||
|
||||
@@ -17,8 +17,7 @@ You're free to use any value in between and test the results.`);
|
||||
}
|
||||
|
||||
let bps = process.argv[2];
|
||||
let buffer = new Buffer(uhk.pushUint32([uhk.usbCommands.setI2cBaudRate], +bps));
|
||||
//console.log(buffer);
|
||||
device.write(uhk.getTransferData(buffer));
|
||||
let response = device.readSync();
|
||||
//console.log(Buffer.from(response));
|
||||
|
||||
(async function() {
|
||||
await uhk.writeDevice(device, [uhk.usbCommands.setI2cBaudRate, ...uhk.uint32ToArray(+bps)]);
|
||||
})();
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
require('shelljs/global');
|
||||
|
||||
function checkFirmwareImage(imagePath, extension) {
|
||||
if (!imagePath) {
|
||||
echo('No firmware image specified.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!imagePath.endsWith(extension)) {
|
||||
echo(`Firmware image extension is not ${extension}`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!test('-f', imagePath)) {
|
||||
echo('Firmware image does not exist.');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function getBlhostCmd(pid) {
|
||||
let blhostPath;
|
||||
switch (process.platform) {
|
||||
case 'linux':
|
||||
const arch = exec('uname -m', {silent:true}).stdout.trim();
|
||||
blhostPath = `linux/${arch}/blhost`;
|
||||
break;
|
||||
case 'darwin':
|
||||
blhostPath = 'mac/blhost';
|
||||
break;
|
||||
case 'win32':
|
||||
blhostPath = 'win/blhost.exe';
|
||||
break;
|
||||
default:
|
||||
echo('Your operating system is not supported.');
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return `${__dirname}/blhost/${blhostPath} --usb 0x1d50,0x${pid.toString(16)}`;
|
||||
}
|
||||
|
||||
function execRetry(command) {
|
||||
let firstRun = true;
|
||||
let remainingRetries = 3;
|
||||
let code;
|
||||
do {
|
||||
if (!firstRun) {
|
||||
console.log(`Retrying ${command}`)
|
||||
}
|
||||
config.fatal = !remainingRetries;
|
||||
code = exec(command).code;
|
||||
config.fatal = true;
|
||||
firstRun = false;
|
||||
} while(code && --remainingRetries);
|
||||
}
|
||||
|
||||
const exp = {
|
||||
checkFirmwareImage,
|
||||
getBlhostCmd,
|
||||
execRetry,
|
||||
}
|
||||
|
||||
Object.keys(exp).forEach(function (cmd) {
|
||||
global[cmd] = exp[cmd];
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
const util = require('util');
|
||||
const HID = require('node-hid');
|
||||
// const debug = process.env.DEBUG;
|
||||
const debug = true;
|
||||
@@ -22,12 +23,16 @@ function getUint32(buffer, offset) {
|
||||
return (buffer[offset]) + (buffer[offset+1] * 2**8) + (buffer[offset+2] * 2**16) + (buffer[offset+3] * 2**24);
|
||||
}
|
||||
|
||||
function pushUint32(array, value) {
|
||||
array.push((value >> 0) & 0xff);
|
||||
array.push((value >> 8) & 0xff);
|
||||
array.push((value >> 16) & 0xff);
|
||||
array.push((value >> 24) & 0xff);
|
||||
return array;
|
||||
function uint32ToArray(value) {
|
||||
return [(value >> 0) & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, (value >> 24) & 0xff];
|
||||
}
|
||||
|
||||
function writeDevice(device, data, options={}) {
|
||||
device.write(getTransferData(new Buffer(data)));
|
||||
if (options.noRead) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return util.promisify(device.read.bind(device))();
|
||||
}
|
||||
|
||||
function getUhkDevice() {
|
||||
@@ -60,6 +65,60 @@ function getBootloaderDevice() {
|
||||
return foundDevice;
|
||||
}
|
||||
|
||||
function checkFirmwareImage(imagePath, extension) {
|
||||
if (!imagePath) {
|
||||
echo('No firmware image specified.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!imagePath.endsWith(extension)) {
|
||||
echo(`Firmware image extension is not ${extension}`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!test('-f', imagePath)) {
|
||||
echo('Firmware image does not exist.');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function getBlhostCmd(pid) {
|
||||
let blhostPath;
|
||||
switch (process.platform) {
|
||||
case 'linux':
|
||||
const arch = exec('uname -m', {silent:true}).stdout.trim();
|
||||
blhostPath = `linux/${arch}/blhost`;
|
||||
break;
|
||||
case 'darwin':
|
||||
blhostPath = 'mac/blhost';
|
||||
break;
|
||||
case 'win32':
|
||||
blhostPath = 'win/blhost.exe';
|
||||
break;
|
||||
default:
|
||||
echo('Your operating system is not supported.');
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return `${__dirname}/blhost/${blhostPath} --usb 0x1d50,0x${pid.toString(16)}`;
|
||||
}
|
||||
|
||||
function execRetry(command) {
|
||||
let firstRun = true;
|
||||
let remainingRetries = 3;
|
||||
let code;
|
||||
do {
|
||||
if (!firstRun) {
|
||||
console.log(`Retrying ${command}`)
|
||||
}
|
||||
config.fatal = !remainingRetries;
|
||||
code = exec(command).code;
|
||||
config.fatal = true;
|
||||
firstRun = false;
|
||||
} while(code && --remainingRetries);
|
||||
}
|
||||
|
||||
let configBufferIds = {
|
||||
hardwareConfig: 0,
|
||||
stagingUserConfig: 1,
|
||||
@@ -71,15 +130,157 @@ let eepromOperations = {
|
||||
write: 1,
|
||||
};
|
||||
|
||||
exports = module.exports = moduleExports = {
|
||||
async function updateDeviceFirmware(firmwareImage, extension) {
|
||||
const usbDir = `${__dirname}`;
|
||||
const blhost = uhk.getBlhostCmd(uhk.enumerationNameToProductId.bootloader);
|
||||
|
||||
uhk.checkFirmwareImage(firmwareImage, extension);
|
||||
config.verbose = true;
|
||||
|
||||
await uhk.reenumerate('bootloader');
|
||||
exec(`${blhost} flash-security-disable 0403020108070605`);
|
||||
exec(`${blhost} flash-erase-region 0xc000 475136`);
|
||||
exec(`${blhost} flash-image ${firmwareImage}`);
|
||||
exec(`${blhost} reset`);
|
||||
|
||||
config.verbose = false;
|
||||
echo('Firmware updated successfully.');
|
||||
};
|
||||
|
||||
// USB commands
|
||||
|
||||
function reenumerate(enumerationMode) {
|
||||
const bootloaderTimeoutMs = 5000;
|
||||
const pollingIntervalMs = 100;
|
||||
let pollingTimeoutMs = 10000;
|
||||
|
||||
let jumped = false;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const enumerationModeId = exports.enumerationModes[enumerationMode];
|
||||
|
||||
if (enumerationModeId === undefined) {
|
||||
const enumerationModes = Object.keys(exports.enumerationModes).join(', ');
|
||||
console.log(`Invalid enumeration mode '${enumerationMode}' is not one of: ${enumerationModes}`);
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Trying to reenumerate as ${enumerationMode}...`);
|
||||
const intervalId = setInterval(async function() {
|
||||
pollingTimeoutMs -= pollingIntervalMs;
|
||||
|
||||
const foundDevice = HID.devices().find(device =>
|
||||
device.vendorId === exports.vendorId && device.productId === exports.enumerationModeIdToProductId[enumerationModeId]);
|
||||
|
||||
if (foundDevice) {
|
||||
console.log(`${enumerationMode} is up`);
|
||||
resolve();
|
||||
clearInterval(intervalId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pollingTimeoutMs <= 0) {
|
||||
console.log(`Couldn't reenumerate as ${enumerationMode}`);
|
||||
reject();
|
||||
clearInterval(intervalId);
|
||||
return;
|
||||
}
|
||||
|
||||
let device = exports.getUhkDevice();
|
||||
if (device && !jumped) {
|
||||
console.log(`UHK found, reenumerating as ${enumerationMode}`);
|
||||
await writeDevice(device, [exports.usbCommands.reenumerate, enumerationModeId, ...uint32ToArray(bootloaderTimeoutMs)], {noRead:true});
|
||||
jumped = true;
|
||||
}
|
||||
|
||||
}, pollingIntervalMs);
|
||||
})
|
||||
};
|
||||
|
||||
async function sendKbootCommandToModule(device, kbootCommandId, i2cAddress) {
|
||||
return await uhk.writeDevice(device, [uhk.usbCommands.sendKbootCommandToModule, kbootCommandId, parseInt(i2cAddress)])
|
||||
};
|
||||
|
||||
async function jumpToModuleBootloader(device, moduleSlotId) {
|
||||
await uhk.writeDevice(device, [uhk.usbCommands.jumpToModuleBootloader, moduleSlotId]);
|
||||
};
|
||||
|
||||
async function waitForKbootIdle(device) {
|
||||
const intervalMs = 100;
|
||||
const pingMessageInterval = 500;
|
||||
let timeoutMs = 10000;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const intervalId = setInterval(async function() {
|
||||
const response = await uhk.writeDevice(device, [uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.currentKbootCommand]);
|
||||
const currentKbootCommand = response[1];
|
||||
if (currentKbootCommand == 0) {
|
||||
console.log('Bootloader pinged.');
|
||||
resolve();
|
||||
clearInterval(intervalId);
|
||||
return;
|
||||
} else if (timeoutMs % pingMessageInterval === 0) {
|
||||
console.log("Cannot ping the bootloader. Please reconnect the left keyboard half. It probably needs several tries, so keep reconnecting until you see this message.");
|
||||
};
|
||||
|
||||
timeoutMs -= intervalMs;
|
||||
|
||||
if (timeoutMs < 0) {
|
||||
reject();
|
||||
clearInterval(intervalId);
|
||||
return;
|
||||
}
|
||||
}, intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
async function updateModuleFirmware(i2cAddress, moduleSlotId, firmwareImage) {
|
||||
const usbDir = `${__dirname}`;
|
||||
const blhostUsb = uhk.getBlhostCmd(uhk.enumerationNameToProductId.buspal);
|
||||
const blhostBuspal = `${blhostUsb} --buspal i2c,${i2cAddress}`;
|
||||
|
||||
config.verbose = true;
|
||||
let device = uhk.getUhkDevice();
|
||||
await uhk.sendKbootCommandToModule(device, uhk.kbootCommands.ping, i2cAddress);
|
||||
await uhk.jumpToModuleBootloader(device, moduleSlotId);
|
||||
await uhk.waitForKbootIdle(device);
|
||||
device.close();
|
||||
|
||||
await uhk.reenumerate('buspal');
|
||||
uhk.execRetry(`${blhostBuspal} get-property 1`);
|
||||
exec(`${blhostBuspal} flash-erase-all-unsecure`);
|
||||
exec(`${blhostBuspal} write-memory 0x0 ${firmwareImage}`);
|
||||
exec(`${blhostUsb} reset`);
|
||||
|
||||
await uhk.reenumerate('normalKeyboard');
|
||||
device = uhk.getUhkDevice();
|
||||
await uhk.sendKbootCommandToModule(device, uhk.kbootCommands.reset, i2cAddress);
|
||||
await uhk.sendKbootCommandToModule(device, uhk.kbootCommands.idle, i2cAddress);
|
||||
device.close();
|
||||
config.verbose = false;
|
||||
echo('Firmware updated successfully.');
|
||||
};
|
||||
|
||||
uhk = exports = module.exports = moduleExports = {
|
||||
bufferToString,
|
||||
getUint16,
|
||||
getUint32,
|
||||
pushUint32,
|
||||
uint32ToArray,
|
||||
writeDevice,
|
||||
getUhkDevice,
|
||||
getBootloaderDevice,
|
||||
getTransferData,
|
||||
checkModuleSlot,
|
||||
checkFirmwareImage,
|
||||
getBlhostCmd,
|
||||
execRetry,
|
||||
updateDeviceFirmware,
|
||||
reenumerate,
|
||||
sendKbootCommandToModule,
|
||||
jumpToModuleBootloader,
|
||||
waitForKbootIdle,
|
||||
updateModuleFirmware,
|
||||
usbCommands: {
|
||||
getDeviceProperty : 0x00,
|
||||
reenumerate : 0x01,
|
||||
@@ -175,20 +376,11 @@ function convertBufferToIntArray(buffer) {
|
||||
}
|
||||
|
||||
function getTransferData(buffer) {
|
||||
const data = convertBufferToIntArray(buffer)
|
||||
// if data start with 0 need to add additional leading zero because HID API remove it.
|
||||
// https://github.com/node-hid/node-hid/issues/187
|
||||
if (data.length > 0 && data[0] === 0) {
|
||||
// data.unshift(0) // TODO: This has been commented out because it causes bugs on Linux and Mac. Gotta test it on Windows and fully remove it if possible.
|
||||
}
|
||||
|
||||
// From HID API documentation:
|
||||
// http://www.signal11.us/oss/hidapi/hidapi/doxygen/html/group__API.html#gad14ea48e440cf5066df87cc6488493af
|
||||
// The first byte of data[] must contain the Report ID.
|
||||
// For devices which only support a single report, this must be set to 0x0.
|
||||
data.unshift(0)
|
||||
|
||||
return data
|
||||
return [0, ...convertBufferToIntArray(buffer)];
|
||||
}
|
||||
|
||||
function readLog(buffer) {
|
||||
|
||||
@@ -3,6 +3,7 @@ const program = require('commander');
|
||||
const tmp = require('tmp');
|
||||
const decompress = require('decompress');
|
||||
const decompressTarbz = require('decompress-tarbz2');
|
||||
const uhk = require('./uhk')
|
||||
require('shelljs/global');
|
||||
|
||||
(async function() {
|
||||
@@ -28,9 +29,11 @@ require('shelljs/global');
|
||||
firmwarePath = tmpObj.name;
|
||||
}
|
||||
config.verbose = true;
|
||||
exec(`${__dirname}/update-device-firmware.js ${firmwarePath}/devices/uhk60-right/firmware.hex`);
|
||||
exec(`${__dirname}/reenumerate.js normalKeyboard`);
|
||||
exec(`${__dirname}/update-module-firmware.js leftHalf ${firmwarePath}/modules/uhk60-left.bin`);
|
||||
console.log('Updating right firmware');
|
||||
await uhk.updateDeviceFirmware(`${firmwarePath}/devices/uhk60-right/firmware.hex`, 'hex');
|
||||
await uhk.reenumerate('normalKeyboard');
|
||||
console.log('Updating left firmware');
|
||||
await uhk.updateModuleFirmware(uhk.moduleSlotToI2cAddress.leftHalf, uhk.moduleSlotToId.leftHalf, `${firmwarePath}/modules/uhk60-left.bin`);
|
||||
|
||||
if (program.overwriteUserConfig) {
|
||||
exec(`${__dirname}/write-config.js ${firmwarePath}/devices/uhk60-right/config.bin`);
|
||||
@@ -39,7 +42,7 @@ require('shelljs/global');
|
||||
}
|
||||
|
||||
config.verbose = false;
|
||||
} catch(exception) {
|
||||
} catch (exception) {
|
||||
console.error(exception.message);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -2,27 +2,13 @@
|
||||
const uhk = require('./uhk');
|
||||
const program = require('commander');
|
||||
require('shelljs/global');
|
||||
require('./shared')
|
||||
|
||||
const extension = '.hex';
|
||||
config.fatal = true;
|
||||
const extension = '.hex';
|
||||
|
||||
program
|
||||
.usage(`firmwareImage${extension}`)
|
||||
.parse(process.argv)
|
||||
|
||||
const firmwareImage = program.args[0];
|
||||
const usbDir = `${__dirname}`;
|
||||
const blhost = getBlhostCmd(uhk.enumerationNameToProductId.bootloader);
|
||||
|
||||
checkFirmwareImage(firmwareImage, extension);
|
||||
|
||||
config.verbose = true;
|
||||
exec(`${usbDir}/reenumerate.js bootloader`);
|
||||
exec(`${blhost} flash-security-disable 0403020108070605`);
|
||||
exec(`${blhost} flash-erase-region 0xc000 475136`);
|
||||
exec(`${blhost} flash-image ${firmwareImage}`);
|
||||
exec(`${blhost} reset`);
|
||||
config.verbose = false;
|
||||
|
||||
echo('Firmware updated successfully.');
|
||||
uhk.updateDeviceFirmware(firmwareImage, extension);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
const uhk = require('./uhk');
|
||||
const program = require('commander');
|
||||
require('shelljs/global');
|
||||
require('./shared');
|
||||
|
||||
const extension = '.bin';
|
||||
config.fatal = true;
|
||||
@@ -12,27 +11,12 @@ program
|
||||
.parse(process.argv)
|
||||
|
||||
let moduleSlot = program.args[0];
|
||||
const moduleSlotId = uhk.checkModuleSlot(moduleSlot, uhk.moduleSlotToId);
|
||||
const i2cAddress = uhk.checkModuleSlot(moduleSlot, uhk.moduleSlotToI2cAddress);
|
||||
|
||||
const firmwareImage = program.args[1];
|
||||
checkFirmwareImage(firmwareImage, extension);
|
||||
uhk.checkFirmwareImage(firmwareImage, extension);
|
||||
|
||||
const usbDir = `${__dirname}`;
|
||||
const blhostUsb = getBlhostCmd(uhk.enumerationNameToProductId.buspal);
|
||||
const blhostBuspal = `${blhostUsb} --buspal i2c,${i2cAddress}`;
|
||||
|
||||
config.verbose = true;
|
||||
exec(`${usbDir}/send-kboot-command-to-module.js ping ${moduleSlot}`);
|
||||
exec(`${usbDir}/jump-to-module-bootloader.js ${moduleSlot}`);
|
||||
exec(`${usbDir}/wait-for-kboot-idle.js`);
|
||||
exec(`${usbDir}/reenumerate.js buspal`);
|
||||
execRetry(`${blhostBuspal} get-property 1`);
|
||||
exec(`${blhostBuspal} flash-erase-all-unsecure`);
|
||||
exec(`${blhostBuspal} write-memory 0x0 ${firmwareImage}`);
|
||||
exec(`${blhostUsb} reset`);
|
||||
exec(`${usbDir}/reenumerate.js normalKeyboard`);
|
||||
execRetry(`${usbDir}/send-kboot-command-to-module.js reset ${moduleSlot}`);
|
||||
exec(`${usbDir}/send-kboot-command-to-module.js idle`);
|
||||
config.verbose = false;
|
||||
|
||||
echo('Firmware updated successfully.');
|
||||
(async function() {
|
||||
await uhk.updateModuleFirmware(i2cAddress, moduleSlotId, firmwareImage);
|
||||
})();
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
const uhk = require('./uhk');
|
||||
|
||||
function getCurrentKbootCommand() {
|
||||
device.write(uhk.getTransferData(new Buffer([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.currentKbootCommand])));
|
||||
const response = Buffer.from(device.readSync());
|
||||
const currentKbootCommand = response[1];
|
||||
if (currentKbootCommand == 0) {
|
||||
console.log('Bootloader pinged.');
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log("Cannot ping the bootloader. Please reconnect the left keyboard half. It probably needs several tries, so keep reconnecting until you see this message.");
|
||||
}
|
||||
}
|
||||
|
||||
const device = uhk.getUhkDevice();
|
||||
|
||||
getCurrentKbootCommand();
|
||||
|
||||
setInterval(() => {
|
||||
getCurrentKbootCommand();
|
||||
}, 500);
|
||||
(async function() {
|
||||
await uhk.waitForKbootIdle(device);
|
||||
})();
|
||||
|
||||
@@ -110,7 +110,7 @@ if (TEST_BUILD || gitTag) {
|
||||
directories: {
|
||||
app: electron_build_folder
|
||||
},
|
||||
appId: 'com.ultimategadgetlabs.uhk.agent',
|
||||
appId: 'com.ultimategadgetlabs.agent',
|
||||
productName: 'UHK Agent',
|
||||
mac: {
|
||||
category: 'public.app-category.utilities',
|
||||
|
||||
Reference in New Issue
Block a user