Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5393501f68 | ||
|
|
99e020d66f | ||
|
|
2c74ce8d3e | ||
|
|
3cd2d208b9 | ||
|
|
d0cd30f915 | ||
|
|
010a23aaeb | ||
|
|
c723fe2651 | ||
|
|
95caa58624 | ||
|
|
9089f088b6 | ||
|
|
1aeb4e8326 | ||
|
|
96b9226adb | ||
|
|
7c065f4368 | ||
|
|
a8108b9abf | ||
|
|
c7baa00720 | ||
|
|
5cdf2282f8 | ||
|
|
89221faf60 | ||
|
|
3b70c84c61 | ||
|
|
5b1f4cb584 | ||
|
|
3ee6c680a1 | ||
|
|
fdcf64d5c6 | ||
|
|
6c327ee414 | ||
|
|
b6bdd1486c | ||
|
|
bd5be98d99 | ||
|
|
802e6a4649 | ||
|
|
ae11c01725 | ||
|
|
f0139c55ee | ||
|
|
b3f2e3451e | ||
|
|
906beaac0e | ||
|
|
46f855d1db | ||
|
|
5341d953ff | ||
|
|
bd9a2a0eeb | ||
|
|
4c10954721 | ||
|
|
bbce1e0e0f | ||
|
|
13f064229f |
23
CHANGELOG.md
23
CHANGELOG.md
@@ -4,15 +4,28 @@ 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/).
|
||||
|
||||
## [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
|
||||
|
||||
- Only accept device, keymap, and macro names upon editing if their trimmed length is non-zero.
|
||||
- Add diagnostics USB scripts, most notably /packages/usb/{get-i2c-health,set-i2c-baud-rate}.js, some utilizing new device protocol commands and properties. `DEVICEPROTOCOL:MINOR`
|
||||
- Implement the Device -> Upload device configuration feature.
|
||||
- Make update-module-firmware.js more robust and able to recover bricked modules (including the left half) by utilizing the newly added wait-for-kboot-idle.js. `DEVICEPROTOCOL:MINOR`
|
||||
- Add the Agent -> About page containing the version number of Agent.
|
||||
- On the mouse speed section of the key action popover, remove the now incorrect bottom sentence and slightly rephrase the top sentence.
|
||||
- Remove --buspal speed specification argument because it gets disrespected by the firmware anyways.
|
||||
- Fix get-left-firmware-version.js to display the correct firmware version.
|
||||
|
||||
## [1.0.4] - 2017-12-30
|
||||
|
||||
Firmware: [8.0.0](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/8.0.0) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
Firmware: 8.0.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- Add mouse speed settings.
|
||||
|
||||
## [1.0.3] - 2017-12-28
|
||||
|
||||
Firmware: [8.0.**0**](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/8.0.**0**) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
Firmware: 8.0.**0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- Add LED brightness settings.
|
||||
- Some key actions, for example Left Arrow were displayed as text with modifiers and as icon without modifires. Now, they're always displayed as icons.
|
||||
@@ -23,18 +36,18 @@ Firmware: [8.0.**0**](https://github.com/UltimateHackingKeyboard/firmware/releas
|
||||
|
||||
## [1.0.2] - 2017-12-25
|
||||
|
||||
Firmware: [**8.0.1**](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/8.0.1) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
Firmware: **8.0.1**[[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.0.1)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- Fix firmware upgrade on Linux.
|
||||
|
||||
## [1.0.1] - 2017-12-22
|
||||
|
||||
Firmware: [7.0.0](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/7.0.0) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
Firmware: 7.0.0[[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v7.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- Fix Linux privilege escalation when udev rules aren't set up.
|
||||
|
||||
## [1.0.0] - 2017-12-14
|
||||
|
||||
Firmware: [**7**.0.0](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/7.0.0) | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
Firmware: **7**.0.0[[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v7.0.0)] | Device Protocol: 4.0.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- First release
|
||||
|
||||
22
README.md
22
README.md
@@ -13,23 +13,29 @@ It's worth mentioning that Agent has two builds.
|
||||
|
||||
The **electron build** is the desktop application which is meant to be used if you have an actual UHK at hand. It starts with an opening screen which detects your UHK. You cannot get past this screen without connecting a UHK via USB.
|
||||
|
||||
The **web build** is meant to be used for demonstation purposes, so people who don't yet own a UHK can get a feel of Agent and its capabilities in their browser. Eventually, WebUSB support will be added to the web build, making it able to communicate with the UHK. Given the sandboxed nature of browers, the web build will always lack features that the electron build offers, so this won't make the electron build obsolete.
|
||||
The **web build** is meant to be used for demonstration purposes, so people who don't yet own a UHK can get a feel of Agent and its capabilities in their browser. Eventually, WebUSB support will be added to the web build, making it able to communicate with the UHK. Given the sandboxed nature of browsers, the web build will always lack features that the electron build offers, so this won't make the electron build obsolete.
|
||||
|
||||
The two builds share code as much as possible.
|
||||
|
||||
## Building the electron application
|
||||
|
||||
First up, make sure that node >=8.1.x and npm >=5.1.x are installed on your system. Next up:
|
||||
### Step 1: Build Dependencies
|
||||
|
||||
You'll need Node.js LTS. Use your OS package manager to install it. [Check the NodeJS site for more info.](https://nodejs.org/en/download/package-manager/ "Installing Node.js via package manager") Mac OS users can simply `brew install node` to get both. Should you need multiple Node.js versions on the same computer, use Node Version Manager for [Mac/Linux](https://github.com/creationix/nvm) or for [Windows](https://github.com/coreybutler/nvm-windows)
|
||||
|
||||
You'll also need `libusb`.
|
||||
On debian-based linux distros, `apt-get install libusb-dev libudev-dev g++` is sufficient.
|
||||
On Mac OS, use `brew install libusb libusb-compat`.
|
||||
For everyone else, use the appropriate package manager for your OS.
|
||||
|
||||
### Step 2: Build Environment
|
||||
|
||||
```
|
||||
# Execute the following line on Linux. Use relevant package manager and package names on non-Debian based distros.
|
||||
apt-get install libusb-dev libudev-dev g++
|
||||
|
||||
git clone git@github.com:UltimateHackingKeyboard/agent.git
|
||||
cd agent
|
||||
npm install
|
||||
npm run build:electron
|
||||
npm run electron
|
||||
npm install # to install Node dependencies
|
||||
npm run build:electron # to build the agent
|
||||
npm run electron # to run the newly built agent
|
||||
```
|
||||
|
||||
At this point, Agent should be running on your machine.
|
||||
|
||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uhk-agent",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -4085,7 +4085,8 @@
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
"private": true,
|
||||
"author": "Ultimate Gadget Laboratories",
|
||||
"main": "electron/dist/electron-main.js",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"firmwareVersion": "8.1.0",
|
||||
"deviceProtocolVersion": "4.2.0",
|
||||
"userConfigVersion": "4.0.0",
|
||||
"hardwareConfigVersion": "1.0.0",
|
||||
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -86,7 +90,6 @@
|
||||
"server:web": "lerna exec --scope uhk-web npm start",
|
||||
"server:electron": "lerna exec --scope uhk-web npm run server:renderer",
|
||||
"electron": "lerna exec --scope uhk-agent npm start",
|
||||
"electron:auto-write-config": "lerna exec --scope uhk-agent npm run auto-write-config",
|
||||
"standard-version": "standard-version",
|
||||
"pack": "node ./scripts/release.js",
|
||||
"sprites": "node ./scripts/generate-svg-sprites",
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "electron ./dist/electron-main.js",
|
||||
"auto-write-config": "electron ./dist/electron-main.js --auto-write-config",
|
||||
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost",
|
||||
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
|
||||
"install:build-deps": "cd ./dist && npm i",
|
||||
|
||||
@@ -21,8 +21,7 @@ import * as isDev from 'electron-is-dev';
|
||||
import { CommandLineInputs } from './models/command-line-inputs';
|
||||
|
||||
const optionDefinitions = [
|
||||
{name: 'addons', type: Boolean},
|
||||
{name: 'auto-write-config', type: Boolean}
|
||||
{name: 'addons', type: Boolean}
|
||||
];
|
||||
|
||||
const options: CommandLineInputs = commandLineArgs(optionDefinitions);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export interface CommandLineInputs {
|
||||
addons?: boolean;
|
||||
'auto-write-config'?: boolean;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,5 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"node-hid": "0.5.7"
|
||||
},
|
||||
"firmwareVersion": "8.0.0",
|
||||
"deviceProtocolVersion": "4.0.0",
|
||||
"userConfigVersion": "4.0.0",
|
||||
"hardwareConfigVersion": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { BrowserWindow, ipcMain } from 'electron';
|
||||
import { BrowserWindow, ipcMain, shell } from 'electron';
|
||||
import { UhkHidDevice } from 'uhk-usb';
|
||||
import { readFile } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { AppStartInfo, IpcEvents, LogService } from 'uhk-common';
|
||||
import { MainServiceBase } from './main-service-base';
|
||||
@@ -18,53 +16,30 @@ export class AppService extends MainServiceBase {
|
||||
|
||||
ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this));
|
||||
ipcMain.on(IpcEvents.app.exit, this.exit.bind(this));
|
||||
ipcMain.on(IpcEvents.app.openUrl, this.openUrl.bind(this));
|
||||
logService.info('[AppService] init success');
|
||||
}
|
||||
|
||||
private async handleAppStartInfo(event: Electron.Event) {
|
||||
this.logService.info('[AppService] getAppStartInfo');
|
||||
|
||||
const packageJson = await this.getPackageJson();
|
||||
|
||||
const response: AppStartInfo = {
|
||||
commandLineArgs: {
|
||||
addons: this.options.addons || false,
|
||||
autoWriteConfig: this.options['auto-write-config'] || false
|
||||
addons: this.options.addons || false
|
||||
},
|
||||
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
|
||||
hasPermission: this.uhkHidDeviceService.hasPermission(),
|
||||
agentVersionInfo: {
|
||||
version: packageJson.version,
|
||||
firmwareVersion: packageJson.firmwareVersion,
|
||||
deviceProtocolVersion: packageJson.deviceProtocolVersion,
|
||||
moduleProtocolVersion: packageJson.moduleProtocolVersion,
|
||||
userConfigVersion: packageJson.userConfigVersion,
|
||||
hardwareConfigVersion: packageJson.hardwareConfigVersion
|
||||
}
|
||||
hasPermission: this.uhkHidDeviceService.hasPermission()
|
||||
};
|
||||
this.logService.info('[AppService] getAppStartInfo response:', response);
|
||||
return event.sender.send(IpcEvents.app.getAppStartInfoReply, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the package.json that delivered with the bundle. Do not use require('package.json')
|
||||
* because the deploy process change the package.json after the build
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
private async getPackageJson(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
readFile(join(__dirname, 'package.json'), {encoding: 'utf-8'}, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(JSON.parse(data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private exit() {
|
||||
this.logService.info('[AppService] exit');
|
||||
this.win.close();
|
||||
}
|
||||
|
||||
private openUrl(event: Electron.Event, urls: Array<string>) {
|
||||
shell.openExternal(urls[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { CommandLineArgs } from './command-line-args';
|
||||
import { VersionInformation } from './version-information';
|
||||
|
||||
export interface AppStartInfo {
|
||||
commandLineArgs: CommandLineArgs;
|
||||
deviceConnected: boolean;
|
||||
hasPermission: boolean;
|
||||
/**
|
||||
* This property contains the version information of the deployed agent components
|
||||
*/
|
||||
agentVersionInfo: VersionInformation;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export interface CommandLineArgs {
|
||||
addons: boolean;
|
||||
autoWriteConfig: boolean;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export namespace Constants {
|
||||
export const VENDOR_ID = 0x1D50;
|
||||
export const PRODUCT_ID = 0x6122;
|
||||
export const MAX_PAYLOAD_SIZE = 64;
|
||||
export const AGENT_GITHUB_URL = 'https://github.com/UltimateHackingKeyboard/agent';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export { IpcEvents } from './ipcEvents';
|
||||
export * from './log';
|
||||
export * from './constants';
|
||||
|
||||
// Source: http://stackoverflow.com/questions/13720256/javascript-regex-camelcase-to-sentence
|
||||
export function camelCaseToSentence(camelCasedText: string): string {
|
||||
|
||||
@@ -3,6 +3,7 @@ class App {
|
||||
public static readonly getAppStartInfo = 'app-get-start-info';
|
||||
public static readonly getAppStartInfoReply = 'app-get-start-info-reply';
|
||||
public static readonly exit = 'app-exit';
|
||||
public static readonly openUrl = 'open-url';
|
||||
}
|
||||
|
||||
class AutoUpdate {
|
||||
|
||||
@@ -33,7 +33,7 @@ export class UhkOperations {
|
||||
this.logService.debug('[UhkOperations] Start flashing left module firmware');
|
||||
|
||||
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.buspal.toString(16)}`];
|
||||
const buspalPrefix = [...prefix, `--buspal i2c,${ModuleSlotToI2cAddress.leftHalf},100k`];
|
||||
const buspalPrefix = [...prefix, `--buspal i2c,${ModuleSlotToI2cAddress.leftHalf}`];
|
||||
|
||||
await this.device.reenumerate(EnumerationModes.NormalKeyboard);
|
||||
this.device.close();
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<div id="main-content" class="main-content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<div class="github-fork-ribbon" *ngIf="!(runningInElectron$ | async)">
|
||||
<a class="" href="https://github.com/UltimateHackingKeyboard/agent" title="Fork me on GitHub">Fork me on GitHub</a>
|
||||
</div>
|
||||
<notifier-container></notifier-container>
|
||||
<progress-button class="save-to-keyboard-button"
|
||||
*ngIf="(saveToKeyboardState$ | async).showButton"
|
||||
|
||||
@@ -1,36 +1,3 @@
|
||||
/* GitHub ribbon */
|
||||
.github-fork-ribbon {
|
||||
background-color: #a00;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
position: fixed;
|
||||
right: -50px;
|
||||
bottom: 40px;
|
||||
z-index: 2000;
|
||||
/* stylelint-disable indentation */
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
-webkit-box-shadow: 0 0 10px #888;
|
||||
-moz-box-shadow: 0 0 10px #888;
|
||||
box-shadow: 0 0 10px #888;
|
||||
/* stylelint-enable indentation */
|
||||
|
||||
a {
|
||||
border: 1px solid #faa;
|
||||
color: #fff;
|
||||
display: block;
|
||||
font: bold 81.25% 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
margin: 1px 0;
|
||||
padding: 10px 50px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 0 5px #444;
|
||||
}
|
||||
}
|
||||
|
||||
main-app {
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { UhkDeviceConnectedGuard } from './services/uhk-device-connected.guard';
|
||||
import { UhkDeviceUninitializedGuard } from './services/uhk-device-uninitialized.guard';
|
||||
import { UhkDeviceInitializedGuard } from './services/uhk-device-initialized.guard';
|
||||
import { MainPage } from './pages/main-page/main.page';
|
||||
import { settingsRoutes } from './components/settings/settings.routes';
|
||||
import { agentRoutes } from './components/agent/agent.routes';
|
||||
import { LoadingDevicePageComponent } from './pages/loading-page/loading-device.page';
|
||||
import { UhkDeviceLoadingGuard } from './services/uhk-device-loading.guard';
|
||||
import { UhkDeviceLoadedGuard } from './services/uhk-device-loaded.guard';
|
||||
@@ -42,7 +42,7 @@ const appRoutes: Routes = [
|
||||
...keymapRoutes,
|
||||
...macroRoutes,
|
||||
...addOnRoutes,
|
||||
...settingsRoutes
|
||||
...agentRoutes
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="row">
|
||||
<h1 class="col-xs-12 pane-title">
|
||||
<i class="uhk-icon uhk-icon-agent-icon"></i>
|
||||
<span>About</span>
|
||||
</h1>
|
||||
<div class="col-xs-12">
|
||||
<div class="agent-version">Agent version: <span class="text-bold">{{version}}</span></div>
|
||||
<div><a class="link-github" (click)="openAgentGitHubPage($event)">Agent on GitHub</a></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
:host {
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.agent {
|
||||
&-version {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
span {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link-github {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Constants } from 'uhk-common';
|
||||
|
||||
import { AppState } from '../../../store';
|
||||
import { getVersions } from '../../../util';
|
||||
import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
|
||||
|
||||
@Component({
|
||||
selector: 'about-page',
|
||||
templateUrl: './about.component.html',
|
||||
styleUrls: ['./about.component.scss'],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class AboutComponent {
|
||||
version: string = getVersions().version;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
}
|
||||
|
||||
openAgentGitHubPage(event) {
|
||||
event.preventDefault();
|
||||
this.store.dispatch(new OpenUrlInNewWindowAction(Constants.AGENT_GITHUB_URL));
|
||||
}
|
||||
}
|
||||
15
packages/uhk-web/src/app/components/agent/agent.routes.ts
Normal file
15
packages/uhk-web/src/app/components/agent/agent.routes.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import { AboutComponent } from './about/about.component';
|
||||
|
||||
export const agentRoutes: Routes = [
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent
|
||||
}
|
||||
];
|
||||
3
packages/uhk-web/src/app/components/agent/index.ts
Normal file
3
packages/uhk-web/src/app/components/agent/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './agent.routes';
|
||||
export * from './about/about.component';
|
||||
export * from './settings/settings.component';
|
||||
@@ -2,13 +2,14 @@ import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { AppState, getAutoUpdateSettings, getCheckingForUpdate } from '../../store';
|
||||
import { AppState, getAutoUpdateSettings, getCheckingForUpdate } from '../../../store';
|
||||
import {
|
||||
CheckForUpdateNowAction,
|
||||
ToggleCheckForUpdateOnStartupAction,
|
||||
TogglePreReleaseFlagAction
|
||||
} from '../../store/actions/auto-update-settings';
|
||||
import { AutoUpdateSettings } from '../../models/auto-update-settings';
|
||||
} from '../../../store/actions/auto-update-settings';
|
||||
import { AutoUpdateSettings } from '../../../models/auto-update-settings';
|
||||
import { getVersions } from '../../../util';
|
||||
|
||||
@Component({
|
||||
selector: 'settings',
|
||||
@@ -19,8 +20,7 @@ import { AutoUpdateSettings } from '../../models/auto-update-settings';
|
||||
}
|
||||
})
|
||||
export class SettingsComponent {
|
||||
// TODO: From where do we get the version number? The electron gives back in main process, but the web...
|
||||
version = '1.0.0';
|
||||
version: string = getVersions().version;
|
||||
autoUpdateSettings$: Observable<AutoUpdateSettings>;
|
||||
checkingForUpdate$: Observable<boolean>;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Version:</label>
|
||||
<div class="col-sm-10">
|
||||
<p class="form-control-static">{{version}}</p>
|
||||
<p>{{version}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
<span role="button" class="btn-link" (click)="saveConfigurationInBINFormat()">binary</span> format.
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-default"
|
||||
>Upload device configuration
|
||||
</button>
|
||||
<label class="btn btn-default btn-file">
|
||||
Upload device configuration
|
||||
<input type="file"
|
||||
(change)="changeFile($event)">
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-danger"
|
||||
|
||||
@@ -3,7 +3,11 @@ import { Store } from '@ngrx/store';
|
||||
|
||||
import { AppState } from '../../../store';
|
||||
import { ResetUserConfigurationAction } from '../../../store/actions/device';
|
||||
import { SaveUserConfigInBinaryFileAction, SaveUserConfigInJsonFileAction } from '../../../store/actions/user-config';
|
||||
import {
|
||||
LoadUserConfigurationFromFileAction,
|
||||
SaveUserConfigInBinaryFileAction,
|
||||
SaveUserConfigInJsonFileAction
|
||||
} from '../../../store/actions/user-config';
|
||||
|
||||
@Component({
|
||||
selector: 'device-settings',
|
||||
@@ -29,4 +33,17 @@ export class DeviceConfigurationComponent {
|
||||
saveConfigurationInBINFormat() {
|
||||
this.store.dispatch(new SaveUserConfigInBinaryFileAction());
|
||||
}
|
||||
|
||||
changeFile(event): void {
|
||||
const files = event.srcElement.files;
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = function () {
|
||||
const arrayBuffer = new Uint8Array(fileReader.result);
|
||||
this.store.dispatch(new LoadUserConfigurationFromFileAction({
|
||||
filename: event.srcElement.value,
|
||||
data: Array.from(arrayBuffer)
|
||||
}));
|
||||
}.bind(this);
|
||||
fileReader.readAsArrayBuffer(files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export class KeymapHeaderComponent implements OnChanges {
|
||||
}
|
||||
|
||||
editKeymapName(name: string) {
|
||||
if (name.length === 0) {
|
||||
if (!util.isValidName(name)) {
|
||||
this.setName();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class MacroHeaderComponent implements AfterViewInit, OnChanges {
|
||||
}
|
||||
|
||||
editMacroName(name: string) {
|
||||
if (name.length === 0) {
|
||||
if (!util.isValidName(name)) {
|
||||
this.setName();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
<div *ngSwitchCase="3" class="mouse__config mouse__config--speed text-center">
|
||||
<div class="help-text--mouse-speed text-left">
|
||||
<p>Press this key along with mouse movement/scrolling to accelerate/decelerate the speed of the action.</p>
|
||||
<p>Press this key along with mouse movement/scrolling to accelerate/decelerate its speed.</p>
|
||||
</div>
|
||||
<div class="btn-group btn-group-lg" role="group">
|
||||
<button class="btn btn-default"
|
||||
@@ -101,9 +101,6 @@
|
||||
<span>Accelerate</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="help-text--mouse-speed last-help text-left">
|
||||
<p>You can set the multiplier in the <a [routerLink]="['/settings']" title="Settings">settings</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngSwitchDefault>
|
||||
</div>
|
||||
|
||||
@@ -47,16 +47,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.help-text--mouse-speed {
|
||||
margin-bottom: 2rem;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
.btn-placeholder {
|
||||
visibility: hidden;
|
||||
@@ -78,8 +68,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.help-text--mouse-speed.last-help {
|
||||
margin-bottom: 0;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './settings.component';
|
||||
export * from './settings.routes';
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { SettingsComponent } from './settings.component';
|
||||
|
||||
export const settingsRoutes: Routes = [
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent
|
||||
}
|
||||
];
|
||||
@@ -117,14 +117,25 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li class="sidebar__level-0--item" [routerLinkActive]="['active']">
|
||||
<div class="sidebar__level-0">
|
||||
<i class="uhk-icon uhk-icon-agent-icon"></i> Agent
|
||||
<i class="fa fa-chevron-up pull-right"
|
||||
(click)="toggleHide($event, 'agent')"></i>
|
||||
</div>
|
||||
<ul [@toggler]="animation['agent']">
|
||||
<li class="sidebar__level-2--item">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/settings']">Settings</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/about']">About</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu--bottom" *ngIf="runInElectron$ | async">
|
||||
<li class="sidebar__level-1--item" [routerLinkActive]="['active']">
|
||||
<a class="sidebar__level-1" [routerLink]="['/settings']">
|
||||
<i class="fa fa-gear"></i> Settings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -64,6 +64,10 @@ ul {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.uhk-icon-agent-icon {
|
||||
margin-left: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
&__level-2 {
|
||||
|
||||
@@ -118,7 +118,11 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
|
||||
this.store.dispatch(MacroActions.addMacro());
|
||||
}
|
||||
|
||||
editDeviceName(name): void {
|
||||
editDeviceName(name: string): void {
|
||||
if (!util.isValidName(name) || name.trim() === this.deviceNameValue) {
|
||||
this.setDeviceName();
|
||||
return;
|
||||
}
|
||||
this.store.dispatch(new RenameUserConfigurationAction(name));
|
||||
}
|
||||
|
||||
|
||||
4
packages/uhk-web/src/app/models/upload-file-data.ts
Normal file
4
packages/uhk-web/src/app/models/upload-file-data.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface UploadFileData {
|
||||
filename: string;
|
||||
data: Array<number>;
|
||||
}
|
||||
@@ -26,6 +26,11 @@ export class AppRendererService {
|
||||
this.ipcRenderer.send(IpcEvents.app.exit);
|
||||
}
|
||||
|
||||
openUrl(url: string): void {
|
||||
this.logService.info(`[AppRendererService] open url: ${url}`);
|
||||
this.ipcRenderer.send(IpcEvents.app.openUrl, url);
|
||||
}
|
||||
|
||||
private registerEvents() {
|
||||
this.ipcRenderer.on(IpcEvents.app.getAppStartInfoReply, (event: string, arg: AppStartInfo) => {
|
||||
this.dispachStoreAction(new ProcessAppStartInfoAction(arg));
|
||||
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
} from './components/popover/tab';
|
||||
import { CaptureKeystrokeButtonComponent } from './components/popover/widgets/capture-keystroke';
|
||||
import { IconComponent } from './components/popover/widgets/icon';
|
||||
import { SettingsComponent } from './components/settings';
|
||||
import { AboutComponent, SettingsComponent } from './components/agent';
|
||||
import { SideMenuComponent } from './components/side-menu';
|
||||
import { SvgKeyboardComponent } from './components/svg/keyboard';
|
||||
import {
|
||||
@@ -152,6 +152,7 @@ import { SliderWrapperComponent } from './components/slider-wrapper/slider-wrapp
|
||||
MacroTextTabComponent,
|
||||
MacroNotFoundComponent,
|
||||
AddOnComponent,
|
||||
AboutComponent,
|
||||
SettingsComponent,
|
||||
KeyboardSliderComponent,
|
||||
CancelableDirective,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
import { AppStartInfo, CommandLineArgs, HardwareConfiguration, Notification, type, VersionInformation } from 'uhk-common';
|
||||
import { AppStartInfo, CommandLineArgs, HardwareConfiguration, Notification, type } from 'uhk-common';
|
||||
import { ElectronLogEntry } from '../../models/xterm-log';
|
||||
|
||||
const PREFIX = '[app] ';
|
||||
@@ -16,8 +16,8 @@ export const ActionTypes = {
|
||||
UNDO_LAST_SUCCESS: type(PREFIX + 'undo last action success'),
|
||||
DISMISS_UNDO_NOTIFICATION: type(PREFIX + 'dismiss notification action'),
|
||||
LOAD_HARDWARE_CONFIGURATION_SUCCESS: type(PREFIX + 'load hardware configuration success'),
|
||||
UPDATE_AGENT_VERSION_INFORMATION: type(PREFIX + 'update agent version information'),
|
||||
ELECTRON_MAIN_LOG_RECEIVED: type(PREFIX + 'Electron main log received')
|
||||
ELECTRON_MAIN_LOG_RECEIVED: type(PREFIX + 'Electron main log received'),
|
||||
OPEN_URL_IN_NEW_WINDOW: type(PREFIX + 'Open URL in new Window')
|
||||
};
|
||||
|
||||
export class AppBootsrappedAction implements Action {
|
||||
@@ -66,18 +66,18 @@ export class LoadHardwareConfigurationSuccessAction implements Action {
|
||||
constructor(public payload: HardwareConfiguration) {}
|
||||
}
|
||||
|
||||
export class UpdateAgentVersionInformationAction implements Action {
|
||||
type = ActionTypes.UPDATE_AGENT_VERSION_INFORMATION;
|
||||
|
||||
constructor(public payload: VersionInformation) {}
|
||||
}
|
||||
|
||||
export class ElectronMainLogReceivedAction implements Action {
|
||||
type = ActionTypes.ELECTRON_MAIN_LOG_RECEIVED;
|
||||
|
||||
constructor(public payload: ElectronLogEntry) {}
|
||||
}
|
||||
|
||||
export class OpenUrlInNewWindowAction implements Action {
|
||||
type = ActionTypes.OPEN_URL_IN_NEW_WINDOW;
|
||||
|
||||
constructor(public payload: string) {}
|
||||
}
|
||||
|
||||
export type Actions
|
||||
= AppStartedAction
|
||||
| AppBootsrappedAction
|
||||
@@ -88,6 +88,6 @@ export type Actions
|
||||
| UndoLastSuccessAction
|
||||
| DismissUndoNotificationAction
|
||||
| LoadHardwareConfigurationSuccessAction
|
||||
| UpdateAgentVersionInformationAction
|
||||
| ElectronMainLogReceivedAction
|
||||
| OpenUrlInNewWindowAction
|
||||
;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { type, UserConfiguration, ConfigurationReply } from 'uhk-common';
|
||||
import { UserConfigurationValue } from '../../models/user-configuration-value';
|
||||
import { UploadFileData } from '../../models/upload-file-data';
|
||||
|
||||
const PREFIX = '[user-config] ';
|
||||
|
||||
@@ -15,7 +16,9 @@ export const ActionTypes = {
|
||||
SAVE_USER_CONFIG_IN_BIN_FILE: type(PREFIX + 'Save User Config in binary file'),
|
||||
LOAD_RESET_USER_CONFIGURATION: type(PREFIX + 'Load reset user configuration'),
|
||||
RENAME_USER_CONFIGURATION: type(PREFIX + 'Rename user configuration'),
|
||||
SET_USER_CONFIGURATION_VALUE: type(PREFIX + 'Set user configuration value')
|
||||
SET_USER_CONFIGURATION_VALUE: type(PREFIX + 'Set user configuration value'),
|
||||
LOAD_USER_CONFIGURATION_FROM_FILE: type(PREFIX + 'Load user configuration from file'),
|
||||
APPLY_USER_CONFIGURATION_FROM_FILE: type(PREFIX + 'Apply user configuration from file')
|
||||
};
|
||||
|
||||
export class LoadUserConfigAction implements Action {
|
||||
@@ -76,6 +79,20 @@ export class SetUserConfigurationValueAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class LoadUserConfigurationFromFileAction implements Action {
|
||||
type = ActionTypes.LOAD_USER_CONFIGURATION_FROM_FILE;
|
||||
|
||||
constructor(public payload: UploadFileData) {
|
||||
}
|
||||
}
|
||||
|
||||
export class ApplyUserConfigurationFromFileAction implements Action {
|
||||
type = ActionTypes.APPLY_USER_CONFIGURATION_FROM_FILE;
|
||||
|
||||
constructor(public payload: UserConfiguration) {
|
||||
}
|
||||
}
|
||||
|
||||
export type Actions
|
||||
= LoadUserConfigAction
|
||||
| LoadUserConfigSuccessAction
|
||||
@@ -87,4 +104,6 @@ export type Actions
|
||||
| LoadResetUserConfigurationAction
|
||||
| RenameUserConfigurationAction
|
||||
| SetUserConfigurationValueAction
|
||||
| LoadUserConfigurationFromFileAction
|
||||
| ApplyUserConfigurationFromFileAction
|
||||
;
|
||||
|
||||
@@ -16,19 +16,15 @@ import {
|
||||
ApplyCommandLineArgsAction,
|
||||
AppStartedAction,
|
||||
DismissUndoNotificationAction,
|
||||
OpenUrlInNewWindowAction,
|
||||
ProcessAppStartInfoAction,
|
||||
ShowNotificationAction,
|
||||
UndoLastAction,
|
||||
UpdateAgentVersionInformationAction
|
||||
UndoLastAction
|
||||
} from '../actions/app';
|
||||
import { AppRendererService } from '../../services/app-renderer.service';
|
||||
import { AppUpdateRendererService } from '../../services/app-update-renderer.service';
|
||||
import {
|
||||
ActionTypes as DeviceActions,
|
||||
ConnectionStateChangedAction,
|
||||
SaveToKeyboardSuccessAction
|
||||
} from '../actions/device';
|
||||
import { AppState, autoWriteUserConfiguration } from '../index';
|
||||
import { ConnectionStateChangedAction } from '../actions/device';
|
||||
import { AppState, runningInElectron } from '../index';
|
||||
|
||||
@Injectable()
|
||||
export class ApplicationEffects {
|
||||
@@ -66,8 +62,7 @@ export class ApplicationEffects {
|
||||
new ConnectionStateChangedAction({
|
||||
connected: appInfo.deviceConnected,
|
||||
hasPermission: appInfo.hasPermission
|
||||
}),
|
||||
new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo)
|
||||
})
|
||||
];
|
||||
});
|
||||
|
||||
@@ -76,12 +71,16 @@ export class ApplicationEffects {
|
||||
.map(action => action.payload)
|
||||
.mergeMap((action: Action) => [action, new DismissUndoNotificationAction()]);
|
||||
|
||||
@Effect({dispatch: false}) saveToKeyboardSuccess$ = this.actions$
|
||||
.ofType<SaveToKeyboardSuccessAction>(DeviceActions.SAVE_TO_KEYBOARD_SUCCESS)
|
||||
.withLatestFrom(this.store.select(autoWriteUserConfiguration))
|
||||
.do(([action, autoWriteUserConfig]) => {
|
||||
if (autoWriteUserConfig) {
|
||||
this.appRendererService.exit();
|
||||
@Effect({dispatch: false}) openUrlInNewWindow$ = this.actions$
|
||||
.ofType<OpenUrlInNewWindowAction>(ActionTypes.OPEN_URL_IN_NEW_WINDOW)
|
||||
.withLatestFrom(this.store.select(runningInElectron))
|
||||
.do(([action, inElectron]) => {
|
||||
const url = action.payload;
|
||||
|
||||
if (inElectron) {
|
||||
this.appRendererService.openUrl(url);
|
||||
} else {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ export class DeviceEffects {
|
||||
});
|
||||
|
||||
@Effect() saveResetUserConfigurationToDevice$ = this.actions$
|
||||
.ofType(UserConfigActions.LOAD_RESET_USER_CONFIGURATION)
|
||||
.ofType(UserConfigActions.LOAD_RESET_USER_CONFIGURATION, UserConfigActions.APPLY_USER_CONFIGURATION_FROM_FILE)
|
||||
.switchMap(() => Observable.of(new SaveConfigurationAction()));
|
||||
|
||||
@Effect({dispatch: false}) updateFirmware$ = this.actions$
|
||||
|
||||
@@ -15,26 +15,38 @@ import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/observable/empty';
|
||||
|
||||
import {
|
||||
ConfigurationReply, HardwareConfiguration, LogService, NotificationType, UhkBuffer,
|
||||
ConfigurationReply,
|
||||
HardwareConfiguration,
|
||||
LogService,
|
||||
NotificationType,
|
||||
UhkBuffer,
|
||||
UserConfiguration
|
||||
} from 'uhk-common';
|
||||
|
||||
import {
|
||||
ActionTypes, LoadConfigFromDeviceReplyAction, LoadUserConfigSuccessAction, RenameUserConfigurationAction,
|
||||
ActionTypes,
|
||||
ApplyUserConfigurationFromFileAction,
|
||||
LoadConfigFromDeviceReplyAction,
|
||||
LoadUserConfigSuccessAction,
|
||||
LoadUserConfigurationFromFileAction,
|
||||
RenameUserConfigurationAction,
|
||||
SaveUserConfigSuccessAction
|
||||
} from '../actions/user-config';
|
||||
|
||||
import { DataStorageRepositoryService } from '../../services/datastorage-repository.service';
|
||||
import { DefaultUserConfigurationService } from '../../services/default-user-configuration.service';
|
||||
import { AppState, autoWriteUserConfiguration, getPrevUserConfiguration, getUserConfiguration } from '../index';
|
||||
import { AppState, getPrevUserConfiguration, getUserConfiguration } from '../index';
|
||||
import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions';
|
||||
import {
|
||||
DismissUndoNotificationAction, LoadHardwareConfigurationSuccessAction, ShowNotificationAction,
|
||||
DismissUndoNotificationAction,
|
||||
LoadHardwareConfigurationSuccessAction,
|
||||
ShowNotificationAction,
|
||||
UndoLastAction
|
||||
} from '../actions/app';
|
||||
import { SaveConfigurationAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
|
||||
import { 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';
|
||||
|
||||
@Injectable()
|
||||
export class UserConfigEffects {
|
||||
@@ -185,16 +197,34 @@ export class UserConfigEffects {
|
||||
saveAs(blob, 'UserConfiguration.bin');
|
||||
});
|
||||
|
||||
@Effect() loadUserConfigurationSuccess$ = this.actions$
|
||||
.ofType(ActionTypes.LOAD_USER_CONFIG_SUCCESS)
|
||||
.withLatestFrom(this.store.select(autoWriteUserConfiguration))
|
||||
.switchMap(([action, autoWriteUserConfig]) => {
|
||||
this.logService.debug('[UserConfigEffect] LOAD_USER_CONFIG_SUCCESS', {autoWriteUserConfig});
|
||||
if (autoWriteUserConfig) {
|
||||
return Observable.of(new SaveConfigurationAction());
|
||||
}
|
||||
else {
|
||||
return Observable.empty();
|
||||
@Effect() loadUserConfigurationFromFile$ = this.actions$
|
||||
.ofType<LoadUserConfigurationFromFileAction>(ActionTypes.LOAD_USER_CONFIGURATION_FROM_FILE)
|
||||
.map(action => action.payload)
|
||||
.map((info: UploadFileData) => {
|
||||
try {
|
||||
const userConfig = new UserConfiguration();
|
||||
|
||||
if (info.filename.endsWith('.bin')) {
|
||||
userConfig.fromBinary(UhkBuffer.fromArray(info.data));
|
||||
} else {
|
||||
const buffer = new Buffer(info.data);
|
||||
const json = buffer.toString();
|
||||
userConfig.fromJsonObject(JSON.parse(json));
|
||||
}
|
||||
|
||||
if (userConfig.userConfigMajorVersion) {
|
||||
return new ApplyUserConfigurationFromFileAction(userConfig);
|
||||
}
|
||||
|
||||
return new ShowNotificationAction({
|
||||
type: NotificationType.Error,
|
||||
message: 'Invalid configuration specified.'
|
||||
});
|
||||
} catch (err) {
|
||||
return new ShowNotificationAction({
|
||||
type: NotificationType.Error,
|
||||
message: 'Invalid configuration specified.'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -39,11 +39,11 @@ export const metaReducers: MetaReducer<AppState>[] = environment.production
|
||||
: [storeFreeze];
|
||||
|
||||
export const getUserConfiguration = (state: AppState) => state.userConfiguration;
|
||||
export const getDeviceName = (state: AppState) => state.userConfiguration.deviceName;
|
||||
export const getDeviceName = createSelector(getUserConfiguration, fromUserConfig.getDeviceName);
|
||||
|
||||
export const appState = (state: AppState) => state.app;
|
||||
|
||||
export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu);
|
||||
export const autoWriteUserConfiguration = createSelector(appState, fromApp.autoWriteUserConfiguration);
|
||||
export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification);
|
||||
export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration);
|
||||
export const runningInElectron = createSelector(appState, fromApp.runningInElectron);
|
||||
|
||||
@@ -7,11 +7,11 @@ import { ActionTypes, ShowNotificationAction } from '../actions/app';
|
||||
import { ActionTypes as UserConfigActionTypes } from '../actions/user-config';
|
||||
import { ActionTypes as DeviceActionTypes } from '../actions/device';
|
||||
import { KeyboardLayout } from '../../keyboard/keyboard-layout.enum';
|
||||
import { getVersions } from '../../util';
|
||||
|
||||
export interface State {
|
||||
started: boolean;
|
||||
showAddonMenu: boolean;
|
||||
autoWriteUserConfiguration: boolean;
|
||||
undoableNotification?: Notification;
|
||||
navigationCountAfterNotification: number;
|
||||
prevUserConfig?: UserConfiguration;
|
||||
@@ -24,10 +24,10 @@ export interface State {
|
||||
export const initialState: State = {
|
||||
started: false,
|
||||
showAddonMenu: false,
|
||||
autoWriteUserConfiguration: false,
|
||||
navigationCountAfterNotification: 0,
|
||||
runningInElectron: runInElectron(),
|
||||
configLoading: true
|
||||
configLoading: true,
|
||||
agentVersionInfo: getVersions()
|
||||
};
|
||||
|
||||
export function reducer(state = initialState, action: Action & { payload: any }) {
|
||||
@@ -42,8 +42,7 @@ export function reducer(state = initialState, action: Action & { payload: any })
|
||||
case ActionTypes.APPLY_COMMAND_LINE_ARGS: {
|
||||
return {
|
||||
...state,
|
||||
showAddonMenu: action.payload.addons,
|
||||
autoWriteUserConfiguration: action.payload.autoWriteConfig
|
||||
showAddonMenu: action.payload.addons
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,18 +115,12 @@ export function reducer(state = initialState, action: Action & { payload: any })
|
||||
};
|
||||
}
|
||||
|
||||
case ActionTypes.UPDATE_AGENT_VERSION_INFORMATION:
|
||||
return {
|
||||
...state,
|
||||
agentVersionInfo: action.payload
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export const showAddonMenu = (state: State) => state.showAddonMenu;
|
||||
export const autoWriteUserConfiguration = (state: State) => state.autoWriteUserConfiguration;
|
||||
export const getUndoableNotification = (state: State) => state.undoableNotification;
|
||||
export const getPrevUserConfiguration = (state: State) => state.prevUserConfig;
|
||||
export const runningInElectron = (state: State) => state.runningInElectron;
|
||||
|
||||
@@ -4,10 +4,11 @@ import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { KeyAction, Keymap, KeyActionHelper, Layer, Macro, Module, SwitchLayerAction, UserConfiguration } from 'uhk-common';
|
||||
import { KeyAction, KeyActionHelper, Keymap, Layer, Macro, Module, SwitchLayerAction, UserConfiguration } from 'uhk-common';
|
||||
import { KeymapActions, MacroActions } from '../actions';
|
||||
import { AppState } from '../index';
|
||||
import { ActionTypes } from '../actions/user-config';
|
||||
import { isValidName } from '../../util';
|
||||
|
||||
export const initialState: UserConfiguration = new UserConfiguration();
|
||||
|
||||
@@ -15,6 +16,7 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
const changedUserConfiguration: UserConfiguration = Object.assign(new UserConfiguration(), state);
|
||||
|
||||
switch (action.type) {
|
||||
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);
|
||||
@@ -31,7 +33,11 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
break;
|
||||
}
|
||||
case KeymapActions.EDIT_NAME: {
|
||||
const name: string = action.payload.name;
|
||||
if (!isValidName(action.payload.name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const name: string = action.payload.name.trim();
|
||||
|
||||
const duplicate = state.keymaps.some((keymap: Keymap) => {
|
||||
return keymap.name === name && keymap.abbreviation !== action.payload.abbr;
|
||||
@@ -169,7 +175,11 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
break;
|
||||
}
|
||||
case MacroActions.EDIT_NAME: {
|
||||
const name: string = action.payload.name;
|
||||
if (!isValidName(action.payload.name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const name: string = action.payload.name.trim();
|
||||
|
||||
const duplicate = state.macros.some((macro: Macro) => {
|
||||
return macro.id !== action.payload.id && macro.name === name;
|
||||
@@ -242,7 +252,9 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
break;
|
||||
|
||||
case ActionTypes.RENAME_USER_CONFIGURATION: {
|
||||
changedUserConfiguration.deviceName = action.payload;
|
||||
if (isValidName(action.payload)) {
|
||||
changedUserConfiguration.deviceName = action.payload.trim();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -399,3 +411,5 @@ function setKeyActionToLayer(newLayer: Layer, moduleIndex: number, keyIndex: num
|
||||
newModule.keyActions = newModule.keyActions.slice();
|
||||
newModule.keyActions[keyIndex] = newKeyAction;
|
||||
}
|
||||
|
||||
export const getDeviceName = (state: UserConfiguration) => state.deviceName;
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export * from './html-helper';
|
||||
export * from './validators';
|
||||
export * from './version-helper';
|
||||
|
||||
3
packages/uhk-web/src/app/util/validators.ts
Normal file
3
packages/uhk-web/src/app/util/validators.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function isValidName(name: string): boolean {
|
||||
return name && name.trim().length > 0;
|
||||
}
|
||||
22
packages/uhk-web/src/app/util/version-helper.ts
Normal file
22
packages/uhk-web/src/app/util/version-helper.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { VersionInformation } from 'uhk-common';
|
||||
|
||||
const collectVersions = (): VersionInformation => {
|
||||
const pkgJson = require('../../../../../package.json');
|
||||
return {
|
||||
version: pkgJson['version'],
|
||||
firmwareVersion: pkgJson['firmwareVersion'],
|
||||
deviceProtocolVersion: pkgJson['deviceProtocolVersion'],
|
||||
moduleProtocolVersion: pkgJson['moduleProtocolVersion'],
|
||||
userConfigVersion: pkgJson['userConfigVersion'],
|
||||
hardwareConfigVersion: pkgJson['hardwareConfigVersion']
|
||||
};
|
||||
};
|
||||
|
||||
let versions: VersionInformation;
|
||||
|
||||
export const getVersions = (): VersionInformation => {
|
||||
if (!versions) {
|
||||
versions = collectVersions();
|
||||
}
|
||||
return versions;
|
||||
};
|
||||
@@ -4,5 +4,72 @@
|
||||
"description": "Right UHK60 keyboard half",
|
||||
"slot": "right-half",
|
||||
"keyCount": 35,
|
||||
"pointerCount": 0
|
||||
"pointerCount": 0,
|
||||
"keyAssignmentOrder": [
|
||||
[1, 2],
|
||||
[1, 3],
|
||||
[1, 4],
|
||||
[1, 5],
|
||||
[1, 6],
|
||||
[1, 7],
|
||||
[0, 1],
|
||||
[0, 2],
|
||||
[0, 3],
|
||||
[0, 4],
|
||||
[0, 5],
|
||||
[0, 6],
|
||||
[0, 7],
|
||||
[1, 1],
|
||||
[1, 8],
|
||||
[1, 9],
|
||||
[1, 10],
|
||||
[1, 11],
|
||||
[1, 12],
|
||||
[1, 14],
|
||||
[0, 15],
|
||||
[0, 8],
|
||||
[0, 9],
|
||||
[0, 10],
|
||||
[0, 11],
|
||||
[0, 12],
|
||||
[0, 13],
|
||||
[0, 14],
|
||||
[1, 15],
|
||||
[1, 16],
|
||||
[1, 17],
|
||||
[1, 18],
|
||||
[1, 19],
|
||||
[1, 21],
|
||||
[0, 22],
|
||||
[0, 16],
|
||||
[0, 17],
|
||||
[0, 18],
|
||||
[0, 19],
|
||||
[0, 20],
|
||||
[0, 21],
|
||||
[1, 22],
|
||||
[1, 24],
|
||||
[1, 25],
|
||||
[1, 26],
|
||||
[1, 27],
|
||||
[1, 28],
|
||||
[0, 23],
|
||||
[0, 24],
|
||||
[0, 25],
|
||||
[0, 26],
|
||||
[0, 27],
|
||||
[0, 28],
|
||||
[1, 29],
|
||||
[1, 30],
|
||||
[1, 31],
|
||||
[1, 32],
|
||||
[1, 34],
|
||||
[0, 30],
|
||||
[0, 32],
|
||||
[0, 33],
|
||||
[0, 34],
|
||||
[0, 35],
|
||||
[1, 33],
|
||||
[0, 31]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -94,3 +94,24 @@ a.disabled {
|
||||
.noUi-value {
|
||||
top: 2rem;
|
||||
}
|
||||
|
||||
.btn-file {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
input[type=file] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
font-size: 100px;
|
||||
text-align: right;
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
outline: none;
|
||||
background: white;
|
||||
cursor: inherit;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><svg width="16" height="16" viewBox="0 0 16 16" id="icon-0401-usb-stick" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 2a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 1 0v-1a.5.5 0 0 0-.5-.5zM8.5 2a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 1 0v-1a.5.5 0 0 0-.5-.5z"/><path d="M11.5 5H11V.5a.5.5 0 0 0-.5-.5h-6a.5.5 0 0 0-.5.5V5h-.5a.5.5 0 0 0-.5.5v9.375c1 1.5 8 1.5 9 0V5.5a.5.5 0 0 0-.5-.5zM5 13.5a.5.5 0 0 1-1 0v-6a.5.5 0 0 1 1 0v6zM10 5H5V1h5v4z"/></svg></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="31" height="16" viewBox="0 0 31 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><svg width="16" height="16" viewBox="0 0 16 16" id="icon-0401-usb-stick" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 2a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 1 0v-1a.5.5 0 0 0-.5-.5zM8.5 2a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 1 0v-1a.5.5 0 0 0-.5-.5z"/><path d="M11.5 5H11V.5a.5.5 0 0 0-.5-.5h-6a.5.5 0 0 0-.5.5V5h-.5a.5.5 0 0 0-.5.5v9.375c1 1.5 8 1.5 9 0V5.5a.5.5 0 0 0-.5-.5zM5 13.5a.5.5 0 0 1-1 0v-6a.5.5 0 0 1 1 0v6zM10 5H5V1h5v4z"/></svg><svg width="15" height="15" viewBox="0 0 15 15" id="icon-agent-icon" x="16" xmlns="http://www.w3.org/2000/svg"><path fill="#333" d="M3 0C1.338 0 0 1.338 0 3v9c0 1.662 1.338 3 3 3h9c1.662 0 3-1.338 3-3V3c0-1.662-1.338-3-3-3H3zM1.375 3.75a.36.36 0 0 1 .125 0H6c.375 0 .75.375.75.375s.375.375.75.375.75-.375.75-.375.375-.375.75-.375h4.5c.75 0 .75.75.75.75V6c0 2.25-1.5 2.25-1.5 2.25H9c-.375 0-.75-1.125-.75-1.125S7.875 6 7.5 6s-.75 1.125-.75 1.125S6.375 8.25 6 8.25H2.25S.75 8.25.75 6V4.5c0-.562.414-.715.625-.75z"/></svg></svg>
|
||||
|
Before Width: | Height: | Size: 698 B After Width: | Height: | Size: 1.2 KiB |
@@ -6,3 +6,8 @@
|
||||
@extend %svg-common;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.uhk-icon-agent-icon {
|
||||
@extend %svg-common;
|
||||
background-position: 100% 0;
|
||||
}
|
||||
|
||||
10
packages/uhk-web/src/svgs/icons/agent-icon.svg
Normal file
10
packages/uhk-web/src/svgs/icons/agent-icon.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="15px" height="15px" viewBox="0 0 15 15" enable-background="new 0 0 15 15" xml:space="preserve">
|
||||
<path fill="#333333" d="M3,0C1.338,0,0,1.338,0,3v9c0,1.662,1.338,3,3,3h9c1.662,0,3-1.338,3-3V3c0-1.662-1.338-3-3-3H3z
|
||||
M1.375,3.75c0.07-0.012,0.125,0,0.125,0H6c0.375,0,0.75,0.375,0.75,0.375S7.125,4.5,7.5,4.5s0.75-0.375,0.75-0.375
|
||||
S8.625,3.75,9,3.75h4.5c0.75,0,0.75,0.75,0.75,0.75V6c0,2.25-1.5,2.25-1.5,2.25H9c-0.375,0-0.75-1.125-0.75-1.125S7.875,6,7.5,6
|
||||
S6.75,7.125,6.75,7.125S6.375,8.25,6,8.25H2.25c0,0-1.5,0-1.5-2.25V4.5C0.75,3.938,1.164,3.785,1.375,3.75z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 935 B |
13
packages/usb/get-i2c-baud-rate.js
Executable file
13
packages/usb/get-i2c-baud-rate.js
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/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')}`)
|
||||
71
packages/usb/get-i2c-health.js
Executable file
71
packages/usb/get-i2c-health.js
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env node
|
||||
const process = require('process');
|
||||
const uhk = require('./uhk');
|
||||
|
||||
function slaveI2cErrorBufferToString(buffer, slaveId) {
|
||||
let statusCount = buffer[1];
|
||||
|
||||
const slaveIdToName = [
|
||||
'leftHalf',
|
||||
'leftAddon',
|
||||
'rightAddon',
|
||||
'rightLedDriver',
|
||||
'leftLedDriver',
|
||||
'kboot',
|
||||
];
|
||||
|
||||
let str = `${slaveIdToName[slaveId].padEnd(14)}: `;
|
||||
|
||||
const statusCodesToStrings = {
|
||||
0: 'nak',
|
||||
1: 'failure',
|
||||
1100: 'busy',
|
||||
1101: 'idle',
|
||||
1102: 'nak',
|
||||
1103: 'arbitrationLost',
|
||||
1104: 'timeout',
|
||||
20000: 'idleSlave',
|
||||
20001: 'idleCycle',
|
||||
};
|
||||
|
||||
for (let i=0; i<statusCount; i++) {
|
||||
let status = uhk.getUint32(buffer, i*8+2);
|
||||
let count = uhk.getUint32(buffer, i*8+4+2);
|
||||
str += `${statusCodesToStrings[status]}:${count} `;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function convertMs(milliseconds) {
|
||||
let days, hours, minutes, seconds;
|
||||
seconds = Math.floor(milliseconds / 1000);
|
||||
minutes = Math.floor(seconds / 60);
|
||||
seconds = seconds % 60;
|
||||
hours = Math.floor(minutes / 60);
|
||||
minutes = minutes % 60;
|
||||
days = Math.floor(hours / 24);
|
||||
hours = hours % 24;
|
||||
return {days, hours, minutes, seconds};
|
||||
}
|
||||
|
||||
const device = uhk.getUhkDevice();
|
||||
|
||||
device.write(uhk.getTransferData(new Buffer(uhk.pushUint32([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]))));
|
||||
response = device.readSync();
|
||||
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')}`)
|
||||
|
||||
for (let slaveId=0; slaveId<6; slaveId++) {
|
||||
device.write(uhk.getTransferData(new Buffer([uhk.usbCommands.getSlaveI2cErrors, slaveId])));
|
||||
let response = Buffer.from(device.readSync());
|
||||
let str = slaveI2cErrorBufferToString(response, slaveId);
|
||||
console.log(str);
|
||||
}
|
||||
@@ -2,15 +2,17 @@
|
||||
const uhk = require('./uhk');
|
||||
|
||||
const device = uhk.getUhkDevice();
|
||||
const sendData = new Buffer([uhk.usbCommands.getModuleProperty, uhk.modulePropertyIds.protocolVersions]);
|
||||
console.log(sendData)
|
||||
const sendData = new Buffer([uhk.usbCommands.getModuleProperty, uhk.moduleSlotToId.leftHalf, uhk.modulePropertyIds.protocolVersions]);
|
||||
//console.log(sendData)
|
||||
device.write(uhk.getTransferData(sendData));
|
||||
const response = Buffer.from(device.readSync());
|
||||
console.log(response)
|
||||
const firmwareMajorVersion = response[7] + (response[8]<<8);
|
||||
const firmwareMinorVersion = response[9] + (response[10]<<8);
|
||||
const firmwarePatchVersion = response[11] + (response[12]<<8);
|
||||
//console.log(response)
|
||||
const moduleProtocolMajorVersion = uhk.getUint16(response, 2);
|
||||
const moduleProtocolMinorVersion = uhk.getUint16(response, 4);
|
||||
const moduleProtocolPatchVersion = uhk.getUint16(response, 6);
|
||||
const firmwareMajorVersion = uhk.getUint16(response, 8);
|
||||
const firmwareMinorVersion = uhk.getUint16(response, 10);
|
||||
const firmwarePatchVersion = uhk.getUint16(response, 12);
|
||||
|
||||
console.log(`firmwareMajorVersion: ${firmwareMajorVersion}`);
|
||||
console.log(`firmwareMinorVersion: ${firmwareMinorVersion}`);
|
||||
console.log(`firmwarePatchVersion: ${firmwarePatchVersion}`);
|
||||
console.log(`moduleProtocolVersion: ${moduleProtocolMajorVersion}.${moduleProtocolMinorVersion}.${moduleProtocolPatchVersion}`);
|
||||
console.log(`firmwareVersion: ${firmwareMajorVersion}.${firmwareMinorVersion}.${firmwarePatchVersion}`);
|
||||
|
||||
@@ -3,13 +3,12 @@ const uhk = require('./uhk');
|
||||
|
||||
const device = uhk.getUhkDevice();
|
||||
const sendData = new Buffer([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.protocolVersions]);
|
||||
//console.log(sendData)
|
||||
device.write(uhk.getTransferData(sendData));
|
||||
const response = Buffer.from(device.readSync());
|
||||
//console.log(response)
|
||||
const firmwareMajorVersion = uhk.getUint16(response, 1);
|
||||
const firmwareMinorVersion = uhk.getUint16(response, 3);
|
||||
const firmwarePatchVersion = uhk.getUint16(response, 5);
|
||||
|
||||
const firmwareMajorVersion = response[1] + (response[2]<<8);
|
||||
const firmwareMinorVersion = response[3] + (response[4]<<8);
|
||||
const firmwarePatchVersion = response[5] + (response[6]<<8);
|
||||
|
||||
console.log(`firmwareMajorVersion: ${firmwareMajorVersion}`);
|
||||
console.log(`firmwareMinorVersion: ${firmwareMinorVersion}`);
|
||||
console.log(`firmwarePatchVersion: ${firmwarePatchVersion}`);
|
||||
console.log(`firmwareVersion: ${firmwareMajorVersion}.${firmwareMinorVersion}.${firmwarePatchVersion}`);
|
||||
|
||||
56
packages/usb/get-slave-i2c-errors.js
Executable file
56
packages/usb/get-slave-i2c-errors.js
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env node
|
||||
const process = require('process');
|
||||
const uhk = require('./uhk');
|
||||
|
||||
const slaveId = parseInt(process.argv[2]);
|
||||
|
||||
const device = uhk.getUhkDevice();
|
||||
const sendData = new Buffer([uhk.usbCommands.getSlaveI2cErrors, slaveId]);
|
||||
device.write(uhk.getTransferData(sendData));
|
||||
const response = Buffer.from(device.readSync());
|
||||
|
||||
//console.log(response);
|
||||
let status = response[0];
|
||||
|
||||
if (status != 0) {
|
||||
console.log('Invalid slave id');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function slaveI2cErrorBufferToString(buffer) {
|
||||
let statusCount = buffer[1];
|
||||
|
||||
const slaveIdToName = [
|
||||
'leftHalf',
|
||||
'leftAddon',
|
||||
'rightAddon',
|
||||
'rightLedDriver',
|
||||
'leftLedDriver',
|
||||
'kboot',
|
||||
];
|
||||
|
||||
let str = `${slaveIdToName[slaveId].padEnd(14)}: `;
|
||||
|
||||
const statusCodesToStrings = {
|
||||
0: 'nak',
|
||||
1: 'failure',
|
||||
1100: 'busy',
|
||||
1101: 'idle',
|
||||
1102: 'nak',
|
||||
1103: 'arbitrationLost',
|
||||
1104: 'timeout',
|
||||
20000: 'idleSlave',
|
||||
20001: 'idleCycle',
|
||||
};
|
||||
|
||||
for (let i=0; i<statusCount; i++) {
|
||||
let status = uhk.getUint32(buffer, i*8+2);
|
||||
let count = uhk.getUint32(buffer, i*8+4+2);
|
||||
str += `${statusCodesToStrings[status]}:${count} `;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
let str = slaveI2cErrorBufferToString(response);
|
||||
console.log(str);
|
||||
25
packages/usb/get-uptime.js
Executable file
25
packages/usb/get-uptime.js
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require('path');
|
||||
const uhk = require('./uhk');
|
||||
const device = uhk.getUhkDevice();
|
||||
|
||||
function convertMs(milliseconds) {
|
||||
let days, hours, minutes, seconds;
|
||||
seconds = Math.floor(milliseconds / 1000);
|
||||
minutes = Math.floor(seconds / 60);
|
||||
seconds = seconds % 60;
|
||||
hours = Math.floor(minutes / 60);
|
||||
minutes = minutes % 60;
|
||||
days = Math.floor(hours / 24);
|
||||
hours = hours % 24;
|
||||
return {days, hours, minutes, seconds};
|
||||
}
|
||||
|
||||
let buffer = new Buffer(uhk.pushUint32([uhk.usbCommands.getDeviceProperty, uhk.devicePropertyIds.uptime]));
|
||||
//console.log(buffer);
|
||||
device.write(uhk.getTransferData(buffer));
|
||||
let response = device.readSync();
|
||||
//console.log(Buffer.from(response));
|
||||
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')}`)
|
||||
10
packages/usb/log-i2c-errors.sh
Executable file
10
packages/usb/log-i2c-errors.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
(
|
||||
startTime=`date +%s`
|
||||
while true; do
|
||||
s="$((`date +%s`-$startTime))"
|
||||
echo "$((s/60))m"
|
||||
for i in `seq 0 5`; do ./get-slave-i2c-errors.js $i; done
|
||||
done
|
||||
) 2>&1 | tee $1
|
||||
24
packages/usb/set-i2c-baud-rate.js
Executable file
24
packages/usb/set-i2c-baud-rate.js
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require('path');
|
||||
const uhk = require('./uhk');
|
||||
const device = uhk.getUhkDevice();
|
||||
|
||||
let programName = path.basename(process.argv[1]);
|
||||
|
||||
if (process.argv.length !== 3) {
|
||||
console.log(`Usage: ${programName} <baud rate in Hz>
|
||||
Adjust the frequency of the main I2C bus via which the right keyboard half communicates with the left half and the add-on modules.
|
||||
Example values:
|
||||
* 400000 Hz - Highest speed. This will probably make communication unreliable. Not recommended.
|
||||
* 100000 Hz - Default speed. Should work in most cases.
|
||||
* 10000 Hz - Low speed. Should help when connection is spotty. It'll make initial LED display updates visibly slower.
|
||||
You're free to use any value in between and test the results.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -14,6 +14,22 @@ function bufferToString(buffer) {
|
||||
return str;
|
||||
}
|
||||
|
||||
function getUint16(buffer, offset) {
|
||||
return (buffer[offset]) + (buffer[offset+1] * 2**8);
|
||||
}
|
||||
|
||||
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 getUhkDevice() {
|
||||
const foundDevice = HID.devices().find(device =>
|
||||
device.vendorId === 0x1d50 && device.productId === 0x6122 &&
|
||||
@@ -57,6 +73,9 @@ let eepromOperations = {
|
||||
|
||||
exports = module.exports = moduleExports = {
|
||||
bufferToString,
|
||||
getUint16,
|
||||
getUint32,
|
||||
pushUint32,
|
||||
getUhkDevice,
|
||||
getBootloaderDevice,
|
||||
getTransferData,
|
||||
@@ -77,6 +96,8 @@ exports = module.exports = moduleExports = {
|
||||
getAdcValue : 0x0c,
|
||||
setLedPwmBrightness : 0x0d,
|
||||
getModuleProperty : 0x0e,
|
||||
getSlaveI2cErrors : 0x0f,
|
||||
setI2cBaudRate : 0x10,
|
||||
},
|
||||
enumerationModes: {
|
||||
bootloader: 0,
|
||||
@@ -101,6 +122,9 @@ exports = module.exports = moduleExports = {
|
||||
deviceProtocolVersion: 0,
|
||||
protocolVersions: 1,
|
||||
configSizes: 2,
|
||||
currentKbootCommand: 3,
|
||||
i2cBaudRate: 4,
|
||||
uptime: 5,
|
||||
},
|
||||
modulePropertyIds: {
|
||||
protocolVersions: 0,
|
||||
|
||||
@@ -19,11 +19,12 @@ checkFirmwareImage(firmwareImage, extension);
|
||||
|
||||
const usbDir = `${__dirname}`;
|
||||
const blhostUsb = getBlhostCmd(uhk.enumerationNameToProductId.buspal);
|
||||
const blhostBuspal = `${blhostUsb} --buspal i2c,${i2cAddress},100k`;
|
||||
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`);
|
||||
|
||||
22
packages/usb/wait-for-kboot-idle.js
Executable file
22
packages/usb/wait-for-kboot-idle.js
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/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);
|
||||
4
packages/usb/zadic/50-uhk60-rules.cmd
Normal file
4
packages/usb/zadic/50-uhk60-rules.cmd
Normal file
@@ -0,0 +1,4 @@
|
||||
wdi-simple.exe --vid 0x1d50 --pid 0x6120 --iid 0 --type 1 --silent
|
||||
wdi-simple.exe --vid 0x1d50 --pid 0x6121 --iid 0 --type 1 --silent
|
||||
wdi-simple.exe --vid 0x1d50 --pid 0x6122 --iid 0 --type 1 --silent
|
||||
wdi-simple.exe --vid 0x1d50 --pid 0x6123 --iid 0 --type 1 --silent
|
||||
BIN
packages/usb/zadic/wdi-simple.exe
Normal file
BIN
packages/usb/zadic/wdi-simple.exe
Normal file
Binary file not shown.
@@ -6,7 +6,7 @@ const fs = require('fs');
|
||||
const fse = require('fs-extra');
|
||||
|
||||
async function downloadFirmware(version) {
|
||||
const url = `https://github.com/UltimateHackingKeyboard/firmware/releases/download/${version}/uhk-firmware-${version}.tar.bz2`;
|
||||
const url = `https://github.com/UltimateHackingKeyboard/firmware/releases/download/v${version}/uhk-firmware-${version}.tar.bz2`;
|
||||
const outputDir = path.join(__dirname, `../tmp`);
|
||||
const output = path.join(outputDir, `uhk-firmware-${version}.tar.bz2`);
|
||||
|
||||
@@ -35,7 +35,7 @@ async function downloadFile(url, output) {
|
||||
}
|
||||
|
||||
(async function main() {
|
||||
const agentJson = require('../packages/uhk-agent/src/package.json');
|
||||
const agentJson = require('../package.json');
|
||||
|
||||
const extractedFirmwareDir = path.join(__dirname, '../tmp/packages/firmware');
|
||||
await fse.emptyDir(extractedFirmwareDir);
|
||||
|
||||
Reference in New Issue
Block a user