41 Commits

Author SHA1 Message Date
László Monda
6e1f0ded9e Bump Agent version to 1.1.3 and update changelog. 2018-04-06 16:26:21 +02:00
László Monda
d58386ef4b Reference firmware 8.1.5 2018-04-04 15:51:04 +02:00
László Monda
179c982bfb Make the Fn+Backspace shortcut of the QWERTY for PC keymap switch to the TES keymap for testing purposes. 2018-04-03 23:25:30 +02:00
László Monda
a7d07dbf4c Make factory-update.js allow the layout to be set. 2018-04-03 21:04:47 +02:00
László Monda
0ca922d24a Sleep 1s between sending reset and idle kboot commands. Hopefully this will always make the left half resume after firmware updates. 2018-04-03 00:49:51 +02:00
László Monda
a6f1aa15a5 Supply the correct configBufferId values to launchEepromTransfer() and get rid of the obsoleted eepromTransfer mapping. 2018-04-02 23:54:48 +02:00
László Monda
fc2d025cc4 Don't display the buffer related USB transfers of writeConfig() because they generate too much noise. 2018-04-02 23:32:13 +02:00
László Monda
8b5ae106bd Display kboot command names instead of numberic ids. 2018-04-02 23:20:07 +02:00
László Monda
44639bbf53 Make apply-config.js and get-module-state.js use read() instead of readSync() 2018-04-02 22:56:28 +02:00
László Monda
9fcce9234a Make getDeviceState await. Dump transfer descriptions before the actual transfers when the debug mode is on. 2018-04-02 21:35:47 +02:00
László Monda
b26fecfc7a Make factory-update.js switch keymap. 2018-04-02 18:28:12 +02:00
László Monda
1b15911783 Extract code to uhk.writeUca() 2018-04-02 17:31:27 +02:00
László Monda
148dd8d361 Rewrite writeHca() using the JavaScript USB API instead of TypeScript because the latter couldn't reopen the USB device. 2018-04-02 17:25:35 +02:00
László Monda
0d9ac50999 Move writeHca() to uhk.js 2018-04-02 14:47:48 +02:00
László Monda
e19e4bc5a4 Move the gist of write-hca.js into writeHca() 2018-04-02 14:43:11 +02:00
László Monda
bdd79a5a9a Clean up writeConfig() a bit. 2018-04-02 14:32:45 +02:00
László Monda
fb4e05fdc4 Dump USB reads and writes via writeDevice() 2018-04-02 00:30:03 +02:00
László Monda
01fcf9053a Strip down factory-update.js to its essentials. 2018-04-02 00:11:35 +02:00
László Monda
533c2f13d2 Copy update-firmwares.js as factory-update.js 2018-04-01 23:58:13 +02:00
László Monda
b9c32b46a9 Extract uhk.applyConfig() and uhk.launchEepromTransfer() 2018-04-01 23:55:40 +02:00
László Monda
f9b7260be6 Extract uhk.writeUserConfig() 2018-04-01 01:23:50 +02:00
László Monda
847694d590 Rewrite write-config.js using async/await. 2018-04-01 01:13:17 +02:00
László Monda
58178a5c7b Extract uhk.updateFirmwares() 2018-03-31 23:50:02 +02:00
László Monda
9b93b4dac5 Rename update-all-firmwares.js to update-firmwares.js 2018-03-31 23:24:34 +02:00
Róbert Kiss
478dac0621 build: extract electron dependencies to the root package.json (#593) 2018-03-31 22:11:41 +02:00
László Monda
f196fcdaa2 Make switch-keymap.js accept a keymap abbreviation. 2018-03-30 19:17:42 +02:00
László Monda
0b420ff516 Extract uhk.switchKeymap() 2018-03-30 18:59:50 +02:00
László Monda
7656af76e4 Add switch-keymap.js 2018-03-30 12:48:28 +02:00
László Monda
beed546ae4 Remove switch keymap actions that point to the TES keymap. 2018-03-29 19:09:00 +02:00
László Monda
bf94370f2f Add the "Export device configuration" button instead of the export link. Make the button export JSON by default and BIN when pressed with Shift. 2018-03-29 18:44:09 +02:00
László Monda
2476049681 Out of play, play/pause, stop, and pause only leave play/pause in the default configuration. 2018-03-29 18:12:08 +02:00
László Monda
f8d8b6d213 Remove Launch Browser, Launch Calculator, and Eject Disk key actions from the default configurations because they don't work reliably across OSes and very rarely used. 2018-03-29 14:51:06 +02:00
László Monda
05bbce1d50 Remove redundant stop media playback actions. 2018-03-29 13:27:13 +02:00
László Monda
32494fa228 Remove relative switch keymap actions. 2018-03-29 13:12:27 +02:00
László Monda
e0ce38988e Remove shut down key actions, only keep sleep key actions, and bind them to the \ key. 2018-03-29 12:54:51 +02:00
László Monda
5c660c549d Update the test keymap. This one should be the final version. 2018-03-28 20:56:56 +02:00
László Monda
510b914e26 Change terminology from download / upload to export / import for greater clarity. 2018-03-28 18:27:27 +02:00
László Monda
cf64fc0c08 Make the tooltip text regarding non-US characters easier to understand. 2018-03-25 22:24:33 +02:00
Róbert Kiss
b25bc9d81d feat: show firmware version of the device/modules on firmware page (#589) 2018-03-23 07:10:02 +01:00
Róbert Kiss
2f00a5eaf4 feat: enhance device firmware page (#588)
* feat: enhance device firmware page

* remove confirmation dialog from firmware upgrade buttons
2018-03-15 12:20:35 +01:00
László Monda
e8fe0f8d3e Fix menu scancode. (#586)
* Fix menu scancode.

* Change the old menu key scancode 118 to 101.

* validate scancodes
2018-03-11 22:56:12 +01:00
43 changed files with 1132 additions and 3649 deletions

View File

@@ -6,6 +6,15 @@ 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.1.3] - 2018-04-06
Firmware: 8.1.**5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
- Show the firmware versions of the left and right keyboard halves on the firmware page.
- Fix menu scancode.
- Make the tooltip text regarding non-US characters easier to understand.
- On the Device Configuration page change terminology from download/upload to export/import for greater clarity.
## [1.1.2] - 2018-03-09
Firmware: 8.1.**4** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.4)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0

679
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.1.2",
"firmwareVersion": "8.1.4",
"version": "1.1.3",
"firmwareVersion": "8.1.5",
"deviceProtocolVersion": "4.2.0",
"userConfigVersion": "4.0.0",
"hardwareConfigVersion": "1.0.0",
@@ -30,19 +30,21 @@
"@types/usb": "1.1.3",
"autoprefixer": "6.5.3",
"buffer": "5.0.6",
"copyfiles": "^2.0.0",
"copy-webpack-plugin": "4.0.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.7.11",
"electron-builder": "20.4.0",
"electron-debug": "1.4.0",
"electron-devtools-installer": "2.2.0",
"electron-log": "2.2.9",
"electron": "1.8.4",
"electron-builder": "20.8.1",
"electron-debug": "1.5.0",
"electron-devtools-installer": "2.2.3",
"electron-log": "2.2.14",
"electron-rebuild": "1.7.3",
"electron-settings": "3.1.2",
"electron-settings": "3.1.4",
"electron-updater": "2.21.4",
"exports-loader": "0.6.3",
"file-loader": "0.10.0",
"fs-extra": "4.0.2",

File diff suppressed because it is too large Load Diff

View File

@@ -17,12 +17,6 @@
"command-line-args": "4.0.7",
"decompress": "4.2.0",
"decompress-bzip2": "4.0.0",
"electron": "1.7.9",
"electron-is-dev": "0.1.2",
"electron-log": "2.2.9",
"electron-rebuild": "1.6.0",
"electron-settings": "3.1.2",
"electron-updater": "2.15.0",
"node-hid": "0.5.4",
"sudo-prompt": "7.0.0",
"tmp": "0.0.33",

View File

@@ -1,5 +1,5 @@
import { ipcMain } from 'electron';
import { ConfigurationReply, DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
import { ConfigurationReply, DeviceConnectionState, HardwareModules, IpcEvents, IpcResponse, LogService } from 'uhk-common';
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
@@ -73,10 +73,15 @@ export class DeviceService {
try {
await this.device.waitUntilKeyboardBusy();
const result = await this.operations.loadConfigurations();
const modules: HardwareModules = {
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
};
response = {
success: true,
...result
...result,
modules
};
} catch (error) {
response = {

View File

@@ -10,7 +10,10 @@
"url": "git@github.com:UltimateHackingKeyboard/agent.git"
},
"scripts": {
"build": "tsc",
"build": "run-s tsc copy:*",
"tsc": "tsc",
"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"
},

View File

@@ -9,3 +9,6 @@ export * from './macro';
export * from './module';
export * from './module-configuration';
export * from './user-configuration';
export const SCANCODES = require('./scancodes.json');
export const SECONDARY_ROLES = require('./secondaryRole.json');

View File

@@ -8,6 +8,7 @@ import { SwitchKeymapAction, UnresolvedSwitchKeymapAction } from './switch-keyma
import { MouseAction } from './mouse-action';
import { PlayMacroAction } from './play-macro-action';
import { NoneAction } from './none-action';
import { isScancodeExists } from '../scancode-checker';
export class Helper {
@@ -26,7 +27,12 @@ export class Helper {
buffer.backtrack();
if (keyActionFirstByte >= KeyActionId.KeystrokeAction && keyActionFirstByte < KeyActionId.LastKeystrokeAction) {
return new KeystrokeAction().fromBinary(buffer);
const keystrokeAction = new KeystrokeAction().fromBinary(buffer);
if (isValidKeystrokeAction(keystrokeAction)) {
return keystrokeAction;
}
return new NoneAction();
}
switch (keyActionFirstByte) {
@@ -68,8 +74,14 @@ export class Helper {
}
switch (keyAction.keyActionType) {
case keyActionType.KeystrokeAction:
return new KeystrokeAction().fromJsonObject(keyAction);
case keyActionType.KeystrokeAction: {
const keystrokeAction = new KeystrokeAction().fromJsonObject(keyAction);
if (isValidKeystrokeAction(keystrokeAction)) {
return keystrokeAction;
}
return new NoneAction();
}
case keyActionType.SwitchLayerAction:
return new SwitchLayerAction().fromJsonObject(keyAction);
case keyActionType.SwitchKeymapAction:
@@ -85,3 +97,9 @@ export class Helper {
}
}
}
function isValidKeystrokeAction(keystrokeAction: KeystrokeAction): boolean {
return keystrokeAction.hasSecondaryRoleAction() ||
keystrokeAction.hasActiveModifier() ||
keystrokeAction.hasScancode() && isScancodeExists(keystrokeAction.scancode);
}

View File

@@ -0,0 +1,26 @@
import { SCANCODES } from './';
let scancodeMap: Map<number, any>;
export function isScancodeExists(scancode: number): boolean {
if (!scancodeMap) {
fillScancodeMap();
}
return scancodeMap.has(scancode);
}
function fillScancodeMap(): void {
scancodeMap = new Map<number, any>();
for (const scanGroup of SCANCODES) {
for (const child of scanGroup.children) {
if (child.additional && child.additional.scancode) {
scancodeMap.set(child.additional.scancode, child);
}
else {
scancodeMap.set(Number.parseInt(child.id), child);
}
}
}
}

View File

@@ -242,7 +242,7 @@
"text": "Delete"
},
{
"id": "118",
"id": "101",
"text": "Menu"
},
{

View File

@@ -1,6 +1,9 @@
import { HardwareModules } from './hardware-modules';
export interface ConfigurationReply {
success: boolean;
userConfiguration?: string;
hardwareConfiguration?: string;
modules?: HardwareModules;
error?: string;
}

View File

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

View File

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

View File

@@ -5,3 +5,5 @@ export * from './app-start-info';
export * from './configuration-reply';
export * from './version-information';
export * from './device-connection-state';
export * from './hardware-modules';
export * from './hardware-module-info';

View File

@@ -22,7 +22,7 @@ export enum UsbCommand {
GetDebugBuffer = 0x0b,
GetAdcValue = 0x0c,
SetLedPwmBrightness = 0x0d,
GetModuleProperties = 0x0e
GetModuleProperty = 0x0e
}
export enum EepromOperation {
@@ -81,3 +81,7 @@ export enum KbootCommands {
ping = 1,
reset = 2
}
export enum ModulePropertyId {
protocolVersions = 0
}

View File

@@ -1,5 +1,12 @@
import { LogService } from 'uhk-common';
import { EnumerationModes, EnumerationNameToProductId, KbootCommands, ModuleSlotToI2cAddress, ModuleSlotToId } from './constants';
import { HardwareModuleInfo, LogService, UhkBuffer } from 'uhk-common';
import {
EnumerationModes,
EnumerationNameToProductId,
KbootCommands,
ModulePropertyId,
ModuleSlotToI2cAddress,
ModuleSlotToId
} from './constants';
import * as path from 'path';
import * as fs from 'fs';
import { UhkBlhost } from './uhk-blhost';
@@ -193,6 +200,50 @@ export class UhkOperations {
return false;
}
public async getLeftModuleVersionInfo(): Promise<HardwareModuleInfo> {
try {
this.logService.debug('[DeviceOperation] USB[T]: Read left module version information');
const command = new Buffer([
UsbCommand.GetModuleProperty,
ModuleSlotToId.leftHalf,
ModulePropertyId.protocolVersions
]);
const buffer = await this.device.write(command);
const uhkBuffer = UhkBuffer.fromArray(convertBufferToIntArray(buffer));
// skip the first 2 byte
uhkBuffer.readUInt16();
return {
moduleProtocolVersion: `${uhkBuffer.readUInt16()}.${uhkBuffer.readUInt16()}.${uhkBuffer.readUInt16()}`,
firmwareVersion: `${uhkBuffer.readUInt16()}.${uhkBuffer.readUInt16()}.${uhkBuffer.readUInt16()}`
};
}
catch (error) {
this.logService.error('[DeviceOperation] Could not read left module version information', error);
}
return {
moduleProtocolVersion: '',
firmwareVersion: ''
};
}
public async getRightModuleVersionInfo(): Promise<HardwareModuleInfo> {
this.logService.debug('[DeviceOperation] USB[T]: Read right module version information');
const command = new Buffer([UsbCommand.GetProperty, DevicePropertyIds.ProtocolVersions]);
const buffer = await this.device.write(command);
const uhkBuffer = UhkBuffer.fromArray(convertBufferToIntArray(buffer));
// skip the first byte
uhkBuffer.readUInt8();
return {
firmwareVersion: `${uhkBuffer.readUInt16()}.${uhkBuffer.readUInt16()}.${uhkBuffer.readUInt16()}`
};
}
/**
* IpcMain handler. Send the UserConfiguration to the UHK Device and send a response with the result.
* @param {string} json - UserConfiguration in JSON format

View File

@@ -9,13 +9,13 @@
<ul class="list-unstyled btn-list">
<li>
Download device configuration in
<span role="button" class="btn-link" (click)="saveConfigurationInJSONFormat()">JSON</span> or
<span role="button" class="btn-link" (click)="saveConfigurationInBINFormat()">binary</span> format.
<button class="btn btn-default"
(click)="exportUserConfiguration($event)">Export device configuration
</button>
</li>
<li>
<label class="btn btn-default btn-file">
Upload device configuration
Import device configuration
<input type="file"
(change)="changeFile($event)">
</label>

View File

@@ -34,6 +34,14 @@ export class DeviceConfigurationComponent {
this.store.dispatch(new SaveUserConfigInBinaryFileAction());
}
exportUserConfiguration(event: MouseEvent) {
if (event.shiftKey) {
this.saveConfigurationInBINFormat();
} else {
this.saveConfigurationInJSONFormat();
}
}
changeFile(event): void {
const files = event.srcElement.files;
const fileReader = new FileReader();

View File

@@ -7,30 +7,35 @@
<span>Firmware</span>
</h1>
<p><i>
<p>
Firmware {{ hardwareModules.leftModuleInfo.firmwareVersion }} is running on the left keyboard half.<br>
Firmware {{ hardwareModules.rightModuleInfo.firmwareVersion }} is running on the right keyboard half.
</p>
<p>
<i>
Please note that the firmware update process may sometimes fail. If if fails then
simply retry until it succeeds. If the left half becomes unresponsive after a failed
update then retry and follow the instructions displayed during the update to fix it.
We'll make the firmware update process more robust.
</i></p>
<p>
Flash firmware {{ (getAgentVersionInfo$ | async).firmwareVersion }} (bundled with Agent)
<button class="btn btn-primary"
[disabled]="flashFirmwareButtonDisbabled$ | async"
(click)="onUpdateFirmware()">Flash firmware
</button>
</i>
</p>
<p>
Flash firmware file <input id="firmware-file-select"
type="file"
[disabled]="flashFirmwareButtonDisbabled$ | async"
(change)="changeFile($event)">
<button class="btn btn-primary"
[disabled]="flashFirmwareButtonDisbabled$ | async"
(click)="onUpdateFirmwareWithFile()">Flash firmware
(click)="onUpdateFirmware()">
Flash firmware {{ (getAgentVersionInfo$ | async).firmwareVersion }} (bundled with Agent)
</button>
<label class="btn btn-primary btn-file"
[class.disabled]="flashFirmwareButtonDisbabled$ | async">
Choose firmware file and flash it
<input id="firmware-file-select"
type="file"
accept=".tar.bz2"
[disabled]="flashFirmwareButtonDisbabled$ | async"
(change)="changeFile($event)">
</label>
</p>
</div>

View File

@@ -2,9 +2,16 @@ import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { VersionInformation } from 'uhk-common';
import { HardwareModules, VersionInformation } from 'uhk-common';
import { AppState, firmwareOkButtonDisabled, flashFirmwareButtonDisbabled, getAgentVersionInfo, xtermLog } from '../../../store';
import {
AppState,
firmwareOkButtonDisabled,
flashFirmwareButtonDisbabled,
getAgentVersionInfo,
getHardwareModules,
xtermLog
} from '../../../store';
import { UpdateFirmwareAction, UpdateFirmwareOkButtonAction, UpdateFirmwareWithAction } from '../../../store/actions/device';
import { XtermLog } from '../../../models/xterm-log';
@@ -22,8 +29,9 @@ export class DeviceFirmwareComponent implements OnDestroy {
xtermLogSubscription: Subscription;
getAgentVersionInfo$: Observable<VersionInformation>;
firmwareOkButtonDisabled$: Observable<boolean>;
hardwareModulesSubscription: Subscription;
hardwareModules: HardwareModules;
arrayBuffer: Uint8Array;
@ViewChild('scrollMe') divElement: ElementRef;
constructor(private store: Store<AppState>) {
@@ -38,24 +46,20 @@ export class DeviceFirmwareComponent implements OnDestroy {
});
this.getAgentVersionInfo$ = store.select(getAgentVersionInfo);
this.firmwareOkButtonDisabled$ = store.select(firmwareOkButtonDisabled);
this.hardwareModulesSubscription = store.select(getHardwareModules).subscribe(data => {
this.hardwareModules = data;
});
}
ngOnDestroy(): void {
this.xtermLogSubscription.unsubscribe();
this.hardwareModulesSubscription.unsubscribe();
}
onUpdateFirmware(): void {
this.store.dispatch(new UpdateFirmwareAction());
}
onUpdateFirmwareWithFile(): void {
if (!this.arrayBuffer) {
return;
}
this.store.dispatch(new UpdateFirmwareWithAction(Array.prototype.slice.call(this.arrayBuffer)));
}
onOkButtonClick(): void {
this.store.dispatch(new UpdateFirmwareOkButtonAction());
}
@@ -64,14 +68,13 @@ export class DeviceFirmwareComponent implements OnDestroy {
const files = event.srcElement.files;
if (files.length === 0) {
this.arrayBuffer = null;
return;
}
const fileReader = new FileReader();
fileReader.onloadend = function () {
this.arrayBuffer = new Uint8Array(fileReader.result);
const arrayBuffer = new Uint8Array(fileReader.result);
this.store.dispatch(new UpdateFirmwareWithAction(Array.prototype.slice.call(arrayBuffer)));
}.bind(this);
fileReader.readAsArrayBuffer(files[0]);
}

View File

@@ -9,7 +9,7 @@
></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."
title="Looking for a non-US character? Just pick the character of the desired key according to the US layout."
data-placement="bottom"></icon>
<capture-keystroke-button (capture)="onKeysCapture($event)" tabindex="0"></capture-keystroke-button>
</div>

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnChanges } from '@angular/core';
import { Select2OptionData, Select2TemplateFunction } from 'ng2-select2';
import { KeyAction, KeystrokeAction, KeystrokeType } from 'uhk-common';
import { KeyAction, KeystrokeAction, KeystrokeType, SCANCODES, SECONDARY_ROLES } from 'uhk-common';
import { Tab } from '../tab';
import { MapperService } from '../../../../services/mapper.service';
@@ -35,8 +35,8 @@ export class KeypressTabComponent extends Tab implements OnChanges {
id: '0',
text: 'None'
}];
this.scanCodeGroups = this.scanCodeGroups.concat(require('./scancodes.json'));
this.secondaryRoleGroups = require('./secondaryRole.json');
this.scanCodeGroups = this.scanCodeGroups.concat(SCANCODES);
this.secondaryRoleGroups = SECONDARY_ROLES;
this.leftModifierSelects = Array(this.leftModifiers.length).fill(false);
this.rightModifierSelects = Array(this.rightModifiers.length).fill(false);
this.selectedScancodeOption = this.scanCodeGroups[0];

View File

@@ -128,12 +128,14 @@
<ul [@toggler]="animation['agent']">
<li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/settings']">Settings</a>
<a [routerLink]="['/settings']"
[class.disabled]="updatingFirmware$ | async">Settings</a>
</div>
</li>
<li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/about']">About</a>
<a [routerLink]="['/about']"
[class.disabled]="updatingFirmware$ | async">About</a>
</div>
</li>
</ul>

View File

@@ -9,6 +9,10 @@
a {
color: #333;
&.disabled {
opacity: 0.65;
}
}
// General list styles for the sidebar-menu.
@@ -112,6 +116,10 @@ ul {
&:focus {
text-decoration: none;
}
&.disabled {
opacity: 0.65;
}
}
}
}

View File

@@ -94,7 +94,7 @@ export class CaptureService {
this.mapping.set(88, 27); // X
this.mapping.set(89, 28); // Y
this.mapping.set(90, 29); // Z
this.mapping.set(93, 118); // Menu
this.mapping.set(93, 101); // Menu
this.mapping.set(96, 98); // Num pad 0
this.mapping.set(97, 89); // Num pad 1
this.mapping.set(98, 90); // Num pad 2

View File

@@ -190,6 +190,7 @@ export class MapperService {
this.basicScanCodeTextMap.set(98, ['Insert', '0']);
this.basicScanCodeTextMap.set(99, ['Del', '.']);
this.basicScanCodeTextMap.set(100, ['ISO key', '|']);
this.basicScanCodeTextMap.set(101, ['Menu']);
this.basicScanCodeTextMap.set(104, ['F13']);
this.basicScanCodeTextMap.set(105, ['F14']);
this.basicScanCodeTextMap.set(106, ['F15']);
@@ -202,7 +203,6 @@ export class MapperService {
this.basicScanCodeTextMap.set(113, ['F22']);
this.basicScanCodeTextMap.set(114, ['F23']);
this.basicScanCodeTextMap.set(115, ['F24']);
this.basicScanCodeTextMap.set(118, ['Menu']);
this.basicScanCodeTextMap.set(176, ['00']);
this.basicScanCodeTextMap.set(177, ['000']);
@@ -236,7 +236,7 @@ export class MapperService {
this.basicScancodeIcons.set(80, 'icon-kbd__mod--arrow-left');
this.basicScancodeIcons.set(81, 'icon-kbd__mod--arrow-down');
this.basicScancodeIcons.set(82, 'icon-kbd__mod--arrow-up');
this.basicScancodeIcons.set(118, 'icon-kbd__mod--menu');
this.basicScancodeIcons.set(101, 'icon-kbd__mod--menu');
this.mediaScancodeIcons = new Map<number, string>();
this.mediaScancodeIcons.set(138, 'icon-kbd__fn--browser');

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
import { Action } from '@ngrx/store';
import { DeviceConnectionState, IpcResponse, type } from 'uhk-common';
import { HardwareModules } from '../../../../../uhk-common/src/models';
const PREFIX = '[device] ';
@@ -22,7 +23,8 @@ export const ActionTypes = {
UPDATE_FIRMWARE_REPLY: type(PREFIX + 'update firmware reply'),
UPDATE_FIRMWARE_SUCCESS: type(PREFIX + 'update firmware success'),
UPDATE_FIRMWARE_FAILED: type(PREFIX + 'update firmware failed'),
UPDATE_FIRMWARE_OK_BUTTON: type(PREFIX + 'update firmware ok button click')
UPDATE_FIRMWARE_OK_BUTTON: type(PREFIX + 'update firmware ok button click'),
MODULES_INFO_LOADED: type(PREFIX + 'module info loaded')
};
export class SetPrivilegeOnLinuxAction implements Action {
@@ -114,6 +116,13 @@ export class ResetMouseSpeedSettingsAction implements Action {
type = ActionTypes.RESET_MOUSE_SPEED_SETTINGS;
}
export class HardwareModulesLoadedAction implements Action {
type = ActionTypes.MODULES_INFO_LOADED;
constructor(public payload: HardwareModules) {
}
}
export type Actions
= SetPrivilegeOnLinuxAction
| SetPrivilegeOnLinuxReplyAction
@@ -132,4 +141,5 @@ export type Actions
| UpdateFirmwareSuccessAction
| UpdateFirmwareFailedAction
| UpdateFirmwareOkButtonAction
| HardwareModulesLoadedAction
;

View File

@@ -43,7 +43,7 @@ import {
ShowNotificationAction,
UndoLastAction
} from '../actions/app';
import { ShowSaveToKeyboardButtonAction } from '../actions/device';
import { HardwareModulesLoadedAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
import { DeviceRendererService } from '../../services/device-renderer.service';
import { UndoUserConfigData } from '../../models/undo-user-config-data';
import { UploadFileData } from '../../models/upload-file-data';
@@ -173,6 +173,8 @@ export class UserConfigEffects {
}));
}
result.push(new HardwareModulesLoadedAction(data.modules));
this.router.navigate(['/']);
return result;

View File

@@ -77,3 +77,4 @@ export const xtermLog = createSelector(deviceState, fromDevice.xtermLog);
export const firmwareOkButtonDisabled = createSelector(deviceState, fromDevice.firmwareOkButtonDisabled);
// tslint:disable-next-line: max-line-length
export const flashFirmwareButtonDisbabled = createSelector(runningInElectron, deviceState, (electron, state: fromDevice.State) => !electron || state.updatingFirmware);
export const getHardwareModules = createSelector(deviceState, fromDevice.getHardwareModules);

View File

@@ -1,8 +1,10 @@
import { Action } from '@ngrx/store';
import { HardwareModules } from 'uhk-common';
import {
ActionTypes,
ConnectionStateChangedAction,
HardwareModulesLoadedAction,
SaveConfigurationAction,
UpdateFirmwareFailedAction
} from '../actions/device';
@@ -16,6 +18,7 @@ export interface State {
saveToKeyboard: ProgressButtonState;
updatingFirmware: boolean;
firmwareUpdateFinished: boolean;
modules: HardwareModules;
log: Array<XtermLog>;
}
@@ -25,6 +28,15 @@ export const initialState: State = {
saveToKeyboard: initProgressButtonState,
updatingFirmware: false,
firmwareUpdateFinished: false,
modules: {
leftModuleInfo: {
firmwareVersion: '',
moduleProtocolVersion: ''
},
rightModuleInfo: {
firmwareVersion: ''
}
},
log: [{message: '', cssClass: XtermCssClass.standard}]
};
@@ -148,6 +160,13 @@ export function reducer(state = initialState, action: Action) {
log: [...state.log, logEntry]
};
}
case ActionTypes.MODULES_INFO_LOADED:
return {
...state,
modules: (action as HardwareModulesLoadedAction).payload
};
default:
return state;
}
@@ -159,3 +178,4 @@ export const hasDevicePermission = (state: State) => state.hasPermission;
export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
export const xtermLog = (state: State) => state.log;
export const firmwareOkButtonDisabled = (state: State) => !state.firmwareUpdateFinished;
export const getHardwareModules = (state: State) => state.modules;

View File

@@ -1,8 +1,7 @@
#!/usr/bin/env node
const uhk = require('./uhk');
(async function() {
const device = uhk.getUhkDevice();
const sendData = new Buffer([uhk.usbCommands.applyConfig]);
device.write(uhk.getTransferData(sendData));
const response = Buffer.from(device.readSync());
console.log(response);
uhk.applyConfig(device);
})();

View File

@@ -9,23 +9,5 @@ if (eepromTransfer === undefined) {
process.exit(1);
}
const device = uhk.getUhkDevice();
device.write(uhk.getTransferData(new Buffer([uhk.usbCommands.launchEepromTransfer, eepromTransfer.operation, eepromTransfer.configBuffer])));
const buffer = Buffer.from(device.readSync());
const responseCode = buffer[0];
if (responseCode !== 0) {
console.error(`Write user config to eeprom failed. Response code: ${responseCode}`);
process.exit(1);
}
// const buffer = await uhk.writeDevice(device, [uhk.usbCommands.launchEepromTransfer, eepromTransfer.operation, eepromTransfer.configBuffer]);
function waitUntilKeyboardBusy() {
device.write(uhk.getTransferData(new Buffer([uhk.usbCommands.getDeviceState])));
const keyboardStateBuffer = Buffer.from(device.readSync());
if (keyboardStateBuffer[1] === 1) {
setTimeout(waitUntilKeyboardBusy, 200);
}
}
waitUntilKeyboardBusy();

40
packages/usb/factory-update.js Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env node
const fs = require('fs');
const program = require('commander');
const uhk = require('./uhk')
require('shelljs/global');
(async function() {
config.fatal = true;
program
.usage(`firmwarePath`)
.parse(process.argv);
if (program.args.length < 1) {
console.error('No firmware path specified.');
exit(1);
}
const firmwarePath = program.args[0];
if (program.args.length < 2) {
console.error('No layout specified.');
exit(1);
}
const layout = program.args[1];
if (!['ansi', 'iso'].includes(layout)) {
console.error('The specified layout is neither ansi nor iso.');
exit(1);
}
const isIso = layout === 'iso';
config.verbose = true;
await uhk.updateFirmwares(firmwarePath);
const device = uhk.getUhkDevice();
const configBuffer = fs.readFileSync(`${firmwarePath}/devices/uhk60-right/config.bin`);
await uhk.writeUca(device, configBuffer);
await uhk.writeHca(device, isIso);
await uhk.switchKeymap(device, 'TES');
console.log('All done!')
config.verbose = false;
})();

View File

@@ -1,14 +1,7 @@
#!/usr/bin/env node
const uhk = require('./uhk');
(async function() {
const device = uhk.getUhkDevice();
function getModuleState() {
const payload = new Buffer([uhk.usbCommands.getModuleProperty, 1]);
console.log('Sending ', uhk.bufferToString(payload));
device.write(uhk.getTransferData(payload));
const receivedBuffer = device.readSync();
console.log('Received', uhk.bufferToString(receivedBuffer));
setTimeout(getModuleState, 500)
}
getModuleState();
await uhk.getModuleProperty(device, 1, uhk.modulePropertyIds.protocolVersions);
})();

15
packages/usb/switch-keymap.js Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env node
const uhk = require('./uhk');
(async function() {
const keymapAbbreviation = process.argv[2];
if (keymapAbbreviation === undefined) {
console.log('Usage: switch-keymap.js keymapName');
return;
}
const device = uhk.getUhkDevice();
const sendData = await uhk.switchKeymap(device, keymapAbbreviation);
console.log(sendData)
})();

View File

@@ -1,7 +1,24 @@
const util = require('util');
const HID = require('node-hid');
// const debug = process.env.DEBUG;
const debug = true;
const {HardwareConfiguration, UhkBuffer} = require('uhk-common');
const {getTransferBuffers, ConfigBufferId, UhkHidDevice, UsbCommand} = require('uhk-usb');
const Logger = require('./logger');
const debug = process.env.DEBUG;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const kbootCommandIdToName = {
0: 'idle',
1: 'ping',
2: 'reset',
};
const eepromOperationIdToName = {
0: 'read',
1: 'write',
}
function bufferToString(buffer) {
let str = '';
@@ -28,11 +45,27 @@ function uint32ToArray(value) {
}
function writeDevice(device, data, options={}) {
device.write(getTransferData(new Buffer(data)));
const dataBuffer = new Buffer(data);
if (!options.noDebug) {
writeLog('W: ', dataBuffer);
}
device.write(getTransferData(dataBuffer));
if (options.noRead) {
return Promise.resolve();
}
return util.promisify(device.read.bind(device))();
return new Promise((resolve, reject) => {
device.read((err, data) => {
if (err) {
reject(err);
} else {
if (!options.noDebug) {
writeLog('R: ', data);
}
resolve(data);
}
});
});
}
function getUhkDevice() {
@@ -119,12 +152,18 @@ function execRetry(command) {
} while(code && --remainingRetries);
}
let configBufferIds = {
const configBufferIds = {
hardwareConfig: 0,
stagingUserConfig: 1,
validatedUserConfig: 2,
};
const configBufferIdToName = {
0: 'hardwareConfig',
1: 'stagingUserConfig',
2: 'validatedUserConfig',
}
let eepromOperations = {
read: 0,
write: 1,
@@ -199,14 +238,24 @@ function reenumerate(enumerationMode) {
};
async function sendKbootCommandToModule(device, kbootCommandId, i2cAddress) {
writeLog(`T: sendKbootCommandToModule kbootCommandId:${kbootCommandIdToName[kbootCommandId]} i2cAddress:${i2cAddress}`);
return await uhk.writeDevice(device, [uhk.usbCommands.sendKbootCommandToModule, kbootCommandId, parseInt(i2cAddress)])
};
async function jumpToModuleBootloader(device, moduleSlotId) {
writeLog(`T: jumpToModuleBootloader moduleSlotId:${moduleSlotId}`);
await uhk.writeDevice(device, [uhk.usbCommands.jumpToModuleBootloader, moduleSlotId]);
};
async function switchKeymap(device, keymapAbbreviation) {
writeLog(`T: switchKeymap keymapAbbreviation:${keymapAbbreviation}`);
const keymapAbbreviationAscii = keymapAbbreviation.split('').map(char => char.charCodeAt(0));
const payload = [uhk.usbCommands.switchKeymap, keymapAbbreviation.length, ...keymapAbbreviationAscii];
return await uhk.writeDevice(device, payload);
}
async function waitForKbootIdle(device) {
writeLog(`T: waitForKbootIdle`);
const intervalMs = 100;
const pingMessageInterval = 500;
let timeoutMs = 10000;
@@ -256,12 +305,98 @@ async function updateModuleFirmware(i2cAddress, moduleSlotId, firmwareImage) {
await uhk.reenumerate('normalKeyboard');
device = uhk.getUhkDevice();
await uhk.sendKbootCommandToModule(device, uhk.kbootCommands.reset, i2cAddress);
await sleep(1000);
await uhk.sendKbootCommandToModule(device, uhk.kbootCommands.idle, i2cAddress);
device.close();
config.verbose = false;
echo('Firmware updated successfully.');
};
async function updateFirmwares(firmwarePath) {
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`);
}
async function writeConfig(device, configBuffer, isHardwareConfig) {
writeLog(`T: writeConfig isHardwareConfig:${isHardwareConfig}`);
const chunkSize = 60;
let offset = 0;
let chunkSizeToRead;
let buffer = await uhk.writeDevice(device, [uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.configSizes]);
const hardwareConfigMaxSize = getUint16(buffer, 1);
const userConfigMaxSize = getUint16(buffer, 3);
const configMaxSize = isHardwareConfig ? hardwareConfigMaxSize : userConfigMaxSize;
const configSize = Math.min(configMaxSize, configBuffer.length);
writeLog('WR: ...');
while (offset < configSize) {
const usbCommand = isHardwareConfig ? uhk.usbCommands.writeHardwareConfig : uhk.usbCommands.writeStagingUserConfig;
chunkSizeToRead = Math.min(chunkSize, configSize - offset);
if (chunkSizeToRead === 0) {
break;
}
buffer = [
usbCommand, chunkSizeToRead, offset & 0xff, offset >> 8,
...configBuffer.slice(offset, offset+chunkSizeToRead)
];
await uhk.writeDevice(device, buffer, {noDebug:true})
offset += chunkSizeToRead;
}
}
async function applyConfig(device) {
writeLog(`T: applyConfig`);
await uhk.writeDevice(device, [uhk.usbCommands.applyConfig]);
}
async function launchEepromTransfer(device, operation, configBufferId) {
writeLog(`T: launchEepromTransfer operation:${eepromOperationIdToName[operation]}`);
const buffer = await uhk.writeDevice(device, [uhk.usbCommands.launchEepromTransfer, operation, configBufferId]);
isBusy = true;
writeLog(`T: getDeviceState`);
do {
const buffer = await uhk.writeDevice(device, [uhk.usbCommands.getDeviceState]);
isBusy = buffer[1] === 1;
} while (isBusy);
};
async function writeUca(device, configBuffer) {
await uhk.writeConfig(device, configBuffer, false);
await uhk.applyConfig(device);
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, configBufferIds.validatedUserConfig);
}
async function writeHca(device, isIso) {
const hardwareConfig = new HardwareConfiguration();
hardwareConfig.signature = 'UHK';
hardwareConfig.majorVersion = 1;
hardwareConfig.minorVersion = 0;
hardwareConfig.patchVersion = 0;
hardwareConfig.brandId = 0;
hardwareConfig.deviceId = 1;
hardwareConfig.uniqueId = Math.floor(2**32 * Math.random());
hardwareConfig.isVendorModeOn = false;
hardwareConfig.isIso = isIso;
const logger = new Logger();
const hardwareBuffer = new UhkBuffer();
hardwareConfig.toBinary(hardwareBuffer);
const buffer = hardwareBuffer.getBufferContent();
await uhk.writeConfig(device, buffer, true);
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, configBufferIds.hardwareConfig);
}
async function getModuleProperty(device, slotId, moduleProperty) {
await writeDevice(device, [uhk.usbCommands.getModuleProperty, slotId, moduleProperty]);
}
uhk = exports = module.exports = moduleExports = {
bufferToString,
getUint16,
@@ -279,8 +414,16 @@ uhk = exports = module.exports = moduleExports = {
reenumerate,
sendKbootCommandToModule,
jumpToModuleBootloader,
switchKeymap,
waitForKbootIdle,
updateModuleFirmware,
updateFirmwares,
writeConfig,
applyConfig,
launchEepromTransfer,
writeUca,
writeHca,
getModuleProperty,
usbCommands: {
getDeviceProperty : 0x00,
reenumerate : 0x01,
@@ -299,6 +442,7 @@ uhk = exports = module.exports = moduleExports = {
getModuleProperty : 0x0e,
getSlaveI2cErrors : 0x0f,
setI2cBaudRate : 0x10,
switchKeymap : 0x11,
},
enumerationModes: {
bootloader: 0,
@@ -332,24 +476,6 @@ uhk = exports = module.exports = moduleExports = {
},
configBufferIds,
eepromOperations,
eepromTransfer: {
readHardwareConfig: {
operation: eepromOperations.read,
configBuffer: configBufferIds.hardwareConfig,
},
writeHardwareConfig: {
operation: eepromOperations.write,
configBuffer:configBufferIds.hardwareConfig,
},
readUserConfig: {
operation: eepromOperations.read,
configBuffer: configBufferIds.validatedUserConfig,
},
writeUserConfig: {
operation: eepromOperations.write,
configBuffer: configBufferIds.validatedUserConfig,
},
},
kbootCommands: {
idle: 0,
ping: 1,
@@ -395,7 +521,11 @@ function writeLog(prefix, buffer) {
if (!debug) {
return;
}
if (buffer) {
console.log(prefix + bufferToString(buffer))
} else {
console.log(prefix);
}
}
function checkModuleSlot(moduleSlot, mapping) {

View File

@@ -18,7 +18,7 @@ const firmwarePath = program.args[0];
const layout = program.args[1];
config.verbose = true;
exec(`${__dirname}/update-all-firmwares.js --overwrite-user-config ${firmwarePath}`);
exec(`${__dirname}/update-firmwares.js --overwrite-user-config ${firmwarePath}`);
exec(`${__dirname}/write-hca.js ${layout}`);
config.verbose = false;

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env node
const fs = require('fs');
const program = require('commander');
const tmp = require('tmp');
const decompress = require('decompress');
@@ -29,16 +30,14 @@ require('shelljs/global');
firmwarePath = tmpObj.name;
}
config.verbose = true;
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`);
await uhk.updateFirmwares(firmwarePath);
if (program.overwriteUserConfig) {
exec(`${__dirname}/write-config.js ${firmwarePath}/devices/uhk60-right/config.bin`);
exec(`${__dirname}/apply-config.js`);
exec(`${__dirname}/eeprom.js writeUserConfig`);
const device = uhk.getUhkDevice();
const configBuffer = fs.readFileSync(`${firmwarePath}/devices/uhk60-right/config.bin`);
await uhk.writeConfig(device, configBuffer, false);
await uhk.applyConfig(device);
await uhk.launchEepromTransfer(device, uhk.eepromOperations.write, uhk.eepromTransfer.writeUserConfig);
}
config.verbose = false;

View File

@@ -2,6 +2,8 @@
const program = require('commander');
const fs = require('fs');
const uhk = require('./uhk');
(async function() {
const device = uhk.getUhkDevice();
require('shelljs/global');
@@ -16,36 +18,9 @@ if (program.args.length == 0) {
}
const configBin = program.args[0];
const chunkSize = 60;
const isHardwareConfig = program.hardwareConfig;
const configTypeString = isHardwareConfig ? 'hardware' : 'user';
let offset = 0;
let configBuffer = fs.readFileSync(configBin);
let chunkSizeToRead;
const configBuffer = fs.readFileSync(configBin);
const payload = new Buffer([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.configSizes]);
device.write(uhk.getTransferData(payload));
let buffer = Buffer.from(device.readSync());
const hardwareConfigMaxSize = buffer[1] + (buffer[2]<<8);
const userConfigMaxSize = buffer[3] + (buffer[4]<<8);
const configMaxSize = isHardwareConfig ? hardwareConfigMaxSize : userConfigMaxSize;
const configSize = Math.min(configMaxSize, configBuffer.length);
while (offset < configSize) {
const usbCommand = isHardwareConfig ? uhk.usbCommands.writeHardwareConfig : uhk.usbCommands.writeStagingUserConfig;
chunkSizeToRead = Math.min(chunkSize, configSize - offset);
if (chunkSizeToRead === 0) {
break;
}
buffer = Buffer.concat([
new Buffer([usbCommand, chunkSizeToRead, offset & 0xff, offset >> 8]),
configBuffer.slice(offset, offset+chunkSizeToRead)
]);
device.write(uhk.getTransferData(buffer));
device.readSync();
offset += chunkSizeToRead;
}
await uhk.writeUserConfig(device, configBuffer, isHardwareConfig);
})();

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env node
const {HardwareConfiguration, UhkBuffer} = require('uhk-common');
const {EepromTransfer, getTransferBuffers, ConfigBufferId, UhkHidDevice, UsbCommand} = require('uhk-usb');
const Logger = require('./logger');
const uhk = require('./uhk');
if (process.argv.length < 2) {
console.log(`use: write-hca {iso|ansi}`);
@@ -14,36 +12,7 @@ if (layout !== 'iso' && layout !== 'ansi') {
process.exit(1);
}
const hardwareConfig = new HardwareConfiguration();
hardwareConfig.signature = 'UHK';
hardwareConfig.majorVersion = 1;
hardwareConfig.minorVersion = 0;
hardwareConfig.patchVersion = 0;
hardwareConfig.brandId = 0;
hardwareConfig.deviceId = 1;
hardwareConfig.uniqueId = Math.floor(2**32 * Math.random());
hardwareConfig.isVendorModeOn = false;
hardwareConfig.isIso = layout === 'iso';
const logger = new Logger();
async function writeHca() {
const device = new UhkHidDevice(logger);
const hardwareBuffer = new UhkBuffer();
hardwareConfig.toBinary(hardwareBuffer);
const buffer = hardwareBuffer.getBufferContent();
const fragments = getTransferBuffers(UsbCommand.WriteHardwareConfig, buffer);
logger.debug('USB[T]: Write hardware configuration to keyboard');
for (const fragment of fragments) {
await device.write(fragment);
}
logger.debug('USB[T]: Write hardware configuration to EEPROM');
await device.writeConfigToEeprom(ConfigBufferId.hardwareConfig);
}
writeHca()
uhk.writeHca(layout === 'iso')
.catch((err)=>{
console.error(err);
});