Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b69d01a0d | ||
|
|
838a92836c | ||
|
|
500ccc296b | ||
|
|
8bb9f7f839 | ||
|
|
5d1cc9202b | ||
|
|
c11658f1fc | ||
|
|
969c36561b | ||
|
|
04904aca5d | ||
|
|
ef0b0aa4ba | ||
|
|
3967593c9c | ||
|
|
b8be1c965b | ||
|
|
b32c93f0f8 | ||
|
|
711d3c0690 | ||
|
|
42b4465230 | ||
|
|
2bf7d545a2 | ||
|
|
d09c46af42 | ||
|
|
d57ba81d10 |
@@ -4,6 +4,12 @@ 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.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
|
||||
|
||||
- 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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"private": true,
|
||||
"author": "Ultimate Gadget Laboratories",
|
||||
"main": "electron/dist/electron-main.js",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -86,6 +86,7 @@
|
||||
"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,6 +36,7 @@
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as url from 'url';
|
||||
import * as commandLineArgs from 'command-line-args';
|
||||
import { UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
|
||||
import { CommandLineArgs, LogRegExps } from 'uhk-common';
|
||||
import { LogRegExps } from 'uhk-common';
|
||||
import { DeviceService } from './services/device.service';
|
||||
import { logger } from './services/logger.service';
|
||||
import { AppUpdateService } from './services/app-update.service';
|
||||
@@ -18,12 +18,14 @@ import { AppService } from './services/app.service';
|
||||
import { SudoService } from './services/sudo.service';
|
||||
import { UhkBlhost } from '../../uhk-usb/src';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
import { CommandLineInputs } from './models/command-line-inputs';
|
||||
|
||||
const optionDefinitions = [
|
||||
{name: 'addons', type: Boolean, defaultOption: false}
|
||||
{name: 'addons', type: Boolean},
|
||||
{name: 'auto-write-config', type: Boolean}
|
||||
];
|
||||
|
||||
const options: CommandLineArgs = commandLineArgs(optionDefinitions);
|
||||
const options: CommandLineInputs = commandLineArgs(optionDefinitions);
|
||||
|
||||
// import './dev-extension';
|
||||
// require('electron-debug')({ showDevTools: true, enabled: true });
|
||||
|
||||
4
packages/uhk-agent/src/models/command-line-inputs.ts
Normal file
4
packages/uhk-agent/src/models/command-line-inputs.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface CommandLineInputs {
|
||||
addons?: boolean;
|
||||
'auto-write-config'?: boolean;
|
||||
}
|
||||
@@ -37,8 +37,13 @@ export class AppUpdateService extends MainServiceBase {
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (ev: any, err: string) => {
|
||||
console.error('[AppUpdateService] error', err);
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateError, err.substr(0, 100));
|
||||
this.logService.error('[AppUpdateService] error', err);
|
||||
let msg = 'Electron updater error';
|
||||
if (err) {
|
||||
msg = err.substr(0, 100);
|
||||
}
|
||||
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateError, msg);
|
||||
});
|
||||
|
||||
autoUpdater.on('download-progress', (progressObj: ProgressInfo) => {
|
||||
|
||||
@@ -3,19 +3,21 @@ import { UhkHidDevice } from 'uhk-usb';
|
||||
import { readFile } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { AppStartInfo, CommandLineArgs, IpcEvents, LogService } from 'uhk-common';
|
||||
import { AppStartInfo, IpcEvents, LogService } from 'uhk-common';
|
||||
import { MainServiceBase } from './main-service-base';
|
||||
import { DeviceService } from './device.service';
|
||||
import { CommandLineInputs } from '../models/command-line-inputs';
|
||||
|
||||
export class AppService extends MainServiceBase {
|
||||
constructor(protected logService: LogService,
|
||||
protected win: Electron.BrowserWindow,
|
||||
private deviceService: DeviceService,
|
||||
private options: CommandLineArgs,
|
||||
private options: CommandLineInputs,
|
||||
private uhkHidDeviceService: UhkHidDevice) {
|
||||
super(logService, win);
|
||||
|
||||
ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this));
|
||||
ipcMain.on(IpcEvents.app.exit, this.exit.bind(this));
|
||||
logService.info('[AppService] init success');
|
||||
}
|
||||
|
||||
@@ -25,8 +27,11 @@ export class AppService extends MainServiceBase {
|
||||
const packageJson = await this.getPackageJson();
|
||||
|
||||
const response: AppStartInfo = {
|
||||
commandLineArgs: this.options,
|
||||
deviceConnected: this.deviceService.isConnected,
|
||||
commandLineArgs: {
|
||||
addons: this.options.addons || false,
|
||||
autoWriteConfig: this.options['auto-write-config'] || false
|
||||
},
|
||||
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
|
||||
hasPermission: this.uhkHidDeviceService.hasPermission(),
|
||||
agentVersionInfo: {
|
||||
version: packageJson.version,
|
||||
@@ -57,4 +62,9 @@ export class AppService extends MainServiceBase {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private exit() {
|
||||
this.logService.info('[AppService] exit');
|
||||
this.win.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { ConfigurationReply, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { Constants, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
import { ConfigurationReply, DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Device, devices } from 'node-hid';
|
||||
import { emptyDir } from 'fs-extra';
|
||||
|
||||
import 'rxjs/add/observable/interval';
|
||||
@@ -19,13 +18,11 @@ import { QueueManager } from './queue-manager';
|
||||
/**
|
||||
* IpcMain pair of the UHK Communication
|
||||
* Functionality:
|
||||
* - Detect device is connected or not
|
||||
* - Send UserConfiguration to the UHK Device
|
||||
* - Read UserConfiguration from the UHK Device
|
||||
*/
|
||||
export class DeviceService {
|
||||
private pollTimer$: Subscription;
|
||||
private connected = false;
|
||||
private queueManager = new QueueManager();
|
||||
|
||||
constructor(private logService: LogService,
|
||||
@@ -66,14 +63,6 @@ export class DeviceService {
|
||||
logService.debug('[DeviceService] init success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with true is an UHK Device is connected to the computer.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public get isConnected(): boolean {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with the actual UserConfiguration from UHK Device
|
||||
* @returns {Promise<Buffer>}
|
||||
@@ -89,7 +78,6 @@ export class DeviceService {
|
||||
success: true,
|
||||
...result
|
||||
};
|
||||
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
|
||||
} catch (error) {
|
||||
response = {
|
||||
success: false,
|
||||
@@ -103,7 +91,6 @@ export class DeviceService {
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.connected = false;
|
||||
this.stopPollTimer();
|
||||
this.logService.info('[DeviceService] Device connection checker stopped.');
|
||||
}
|
||||
@@ -154,15 +141,16 @@ export class DeviceService {
|
||||
|
||||
this.pollTimer$ = Observable.interval(1000)
|
||||
.startWith(0)
|
||||
.map(() => {
|
||||
return devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID);
|
||||
})
|
||||
.map(() => this.device.deviceConnected())
|
||||
.distinctUntilChanged()
|
||||
.do((connected: boolean) => {
|
||||
this.connected = connected;
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, connected);
|
||||
this.logService.info(`[DeviceService] Device connection state changed to: ${connected}`);
|
||||
const response: DeviceConnectionState = {
|
||||
connected,
|
||||
hasPermission: this.device.hasPermission()
|
||||
};
|
||||
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
|
||||
this.logService.info('[DeviceService] Device connection state changed to:', response);
|
||||
})
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { ipcMain, app } from 'electron';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
import * as path from 'path';
|
||||
import * as sudo from 'sudo-prompt';
|
||||
import { dirSync } from 'tmp';
|
||||
import { emptyDir, copy } from 'fs-extra';
|
||||
|
||||
import { IpcEvents, LogService, IpcResponse } from 'uhk-common';
|
||||
|
||||
@@ -10,7 +12,7 @@ export class SudoService {
|
||||
|
||||
constructor(private logService: LogService) {
|
||||
if (isDev) {
|
||||
this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '..');
|
||||
this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '../../../../');
|
||||
} else {
|
||||
this.rootDir = path.dirname(app.getAppPath());
|
||||
}
|
||||
@@ -18,10 +20,10 @@ export class SudoService {
|
||||
ipcMain.on(IpcEvents.device.setPrivilegeOnLinux, this.setPrivilege.bind(this));
|
||||
}
|
||||
|
||||
private setPrivilege(event: Electron.Event) {
|
||||
private async setPrivilege(event: Electron.Event) {
|
||||
switch (process.platform) {
|
||||
case 'linux':
|
||||
this.setPrivilegeOnLinux(event);
|
||||
await this.setPrivilegeOnLinux(event);
|
||||
break;
|
||||
default:
|
||||
const response: IpcResponse = {
|
||||
@@ -34,23 +36,31 @@ export class SudoService {
|
||||
}
|
||||
}
|
||||
|
||||
private setPrivilegeOnLinux(event: Electron.Event) {
|
||||
const scriptPath = path.join(this.rootDir, 'rules/setup-rules.sh');
|
||||
private async setPrivilegeOnLinux(event: Electron.Event) {
|
||||
const tmpDirectory = dirSync();
|
||||
const rulesDir = path.join(this.rootDir, 'rules');
|
||||
this.logService.debug('[SudoService] Copy rules dir', { src: rulesDir, dst: tmpDirectory.name });
|
||||
await copy(rulesDir, tmpDirectory.name);
|
||||
|
||||
const scriptPath = path.join(tmpDirectory.name, 'setup-rules.sh');
|
||||
|
||||
const options = {
|
||||
name: 'Setting UHK access rules'
|
||||
};
|
||||
const command = `sh ${scriptPath}`;
|
||||
console.log(command);
|
||||
sudo.exec(command, options, (error: any) => {
|
||||
this.logService.debug('[SudoService] Set privilege command: ', command);
|
||||
sudo.exec(command, options, async (error: any) => {
|
||||
const response = new IpcResponse();
|
||||
|
||||
if (error) {
|
||||
this.logService.error('[SudoService] Error when set privilege: ', error);
|
||||
response.success = false;
|
||||
response.error = error;
|
||||
} else {
|
||||
response.success = true;
|
||||
}
|
||||
|
||||
await emptyDir(tmpDirectory.name);
|
||||
event.sender.send(IpcEvents.device.setPrivilegeOnLinuxReply, response);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export interface CommandLineArgs {
|
||||
addons: boolean;
|
||||
autoWriteConfig: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DeviceConnectionState {
|
||||
connected: boolean;
|
||||
hasPermission: boolean;
|
||||
}
|
||||
@@ -4,3 +4,4 @@ export * from './ipc-response';
|
||||
export * from './app-start-info';
|
||||
export * from './configuration-reply';
|
||||
export * from './version-information';
|
||||
export * from './device-connection-state';
|
||||
|
||||
@@ -2,6 +2,7 @@ class App {
|
||||
public static readonly appStarted = 'app-started';
|
||||
public static readonly getAppStartInfo = 'app-get-start-info';
|
||||
public static readonly getAppStartInfoReply = 'app-get-start-info-reply';
|
||||
public static readonly exit = 'app-exit';
|
||||
}
|
||||
|
||||
class AutoUpdate {
|
||||
|
||||
@@ -21,7 +21,8 @@ export enum UsbCommand {
|
||||
SetTestLed = 0x0a,
|
||||
GetDebugBuffer = 0x0b,
|
||||
GetAdcValue = 0x0c,
|
||||
SetLedPwmBrightness = 0x0d
|
||||
SetLedPwmBrightness = 0x0d,
|
||||
GetModuleProperties = 0x0e
|
||||
}
|
||||
|
||||
export enum EepromOperation {
|
||||
|
||||
@@ -25,6 +25,7 @@ export class UhkHidDevice {
|
||||
* @private
|
||||
*/
|
||||
private _device: HID;
|
||||
private _hasPermission = false;
|
||||
|
||||
constructor(private logService: LogService) {
|
||||
}
|
||||
@@ -38,8 +39,18 @@ export class UhkHidDevice {
|
||||
*/
|
||||
public hasPermission(): boolean {
|
||||
try {
|
||||
devices();
|
||||
if (this._hasPermission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.deviceConnected()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this._hasPermission = this.getDevice() !== null;
|
||||
this.close();
|
||||
|
||||
return this._hasPermission;
|
||||
} catch (err) {
|
||||
this.logService.error('[UhkHidDevice] hasPermission', err);
|
||||
}
|
||||
@@ -47,6 +58,21 @@ export class UhkHidDevice {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with true is an UHK Device is connected to the computer.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public deviceConnected(): boolean {
|
||||
const connected = devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID);
|
||||
|
||||
if (!connected) {
|
||||
this._hasPermission = false;
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to the UHK device and wait for the response.
|
||||
* Throw an error when 1st byte of the response is not 0
|
||||
|
||||
@@ -21,6 +21,11 @@ export class AppRendererService {
|
||||
this.ipcRenderer.send(IpcEvents.app.getAppStartInfo);
|
||||
}
|
||||
|
||||
exit() {
|
||||
this.logService.info('[AppRendererService] exit');
|
||||
this.ipcRenderer.send(IpcEvents.app.exit);
|
||||
}
|
||||
|
||||
private registerEvents() {
|
||||
this.ipcRenderer.on(IpcEvents.app.getAppStartInfoReply, (event: string, arg: AppStartInfo) => {
|
||||
this.dispachStoreAction(new ProcessAppStartInfoAction(arg));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
|
||||
import { IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { AppState } from '../store';
|
||||
import { IpcCommonRenderer } from './ipc-common-renderer';
|
||||
import {
|
||||
@@ -47,7 +47,7 @@ export class DeviceRendererService {
|
||||
}
|
||||
|
||||
private registerEvents(): void {
|
||||
this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: boolean) => {
|
||||
this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: DeviceConnectionState) => {
|
||||
this.dispachStoreAction(new ConnectionStateChangedAction(arg));
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
import { AppStartInfo, HardwareConfiguration, Notification, type, VersionInformation } from 'uhk-common';
|
||||
import { AppStartInfo, CommandLineArgs, HardwareConfiguration, Notification, type, VersionInformation } from 'uhk-common';
|
||||
import { ElectronLogEntry } from '../../models/xterm-log';
|
||||
|
||||
const PREFIX = '[app] ';
|
||||
@@ -10,7 +10,7 @@ export const ActionTypes = {
|
||||
APP_BOOTSRAPPED: type(PREFIX + 'bootstrapped'),
|
||||
APP_STARTED: type(PREFIX + 'started'),
|
||||
APP_SHOW_NOTIFICATION: type(PREFIX + 'show notification'),
|
||||
APP_TOGGLE_ADDON_MENU: type(PREFIX + 'toggle add-on menu'),
|
||||
APPLY_COMMAND_LINE_ARGS: type(PREFIX + 'apply command line args'),
|
||||
APP_PROCESS_START_INFO: type(PREFIX + 'process start info'),
|
||||
UNDO_LAST: type(PREFIX + 'undo last action'),
|
||||
UNDO_LAST_SUCCESS: type(PREFIX + 'undo last action success'),
|
||||
@@ -34,10 +34,10 @@ export class ShowNotificationAction implements Action {
|
||||
constructor(public payload: Notification) { }
|
||||
}
|
||||
|
||||
export class ToggleAddonMenuAction implements Action {
|
||||
type = ActionTypes.APP_TOGGLE_ADDON_MENU;
|
||||
export class ApplyCommandLineArgsAction implements Action {
|
||||
type = ActionTypes.APPLY_COMMAND_LINE_ARGS;
|
||||
|
||||
constructor(public payload: boolean) { }
|
||||
constructor(public payload: CommandLineArgs) { }
|
||||
}
|
||||
|
||||
export class ProcessAppStartInfoAction implements Action {
|
||||
@@ -82,7 +82,7 @@ export type Actions
|
||||
= AppStartedAction
|
||||
| AppBootsrappedAction
|
||||
| ShowNotificationAction
|
||||
| ToggleAddonMenuAction
|
||||
| ApplyCommandLineArgsAction
|
||||
| ProcessAppStartInfoAction
|
||||
| UndoLastAction
|
||||
| UndoLastSuccessAction
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { IpcResponse, type } from 'uhk-common';
|
||||
import { DeviceConnectionState, IpcResponse, type } from 'uhk-common';
|
||||
|
||||
const PREFIX = '[device] ';
|
||||
|
||||
@@ -8,7 +8,6 @@ export const ActionTypes = {
|
||||
SET_PRIVILEGE_ON_LINUX: type(PREFIX + 'set privilege on linux'),
|
||||
SET_PRIVILEGE_ON_LINUX_REPLY: type(PREFIX + 'set privilege on linux reply'),
|
||||
CONNECTION_STATE_CHANGED: type(PREFIX + 'connection state changed'),
|
||||
PERMISSION_STATE_CHANGED: type(PREFIX + 'permission state changed'),
|
||||
SAVE_CONFIGURATION: type(PREFIX + 'save configuration'),
|
||||
SAVE_CONFIGURATION_REPLY: type(PREFIX + 'save configuration reply'),
|
||||
SAVING_CONFIGURATION: type(PREFIX + 'saving configuration'),
|
||||
@@ -39,14 +38,7 @@ export class SetPrivilegeOnLinuxReplyAction implements Action {
|
||||
export class ConnectionStateChangedAction implements Action {
|
||||
type = ActionTypes.CONNECTION_STATE_CHANGED;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export class PermissionStateChangedAction implements Action {
|
||||
type = ActionTypes.PERMISSION_STATE_CHANGED;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
constructor(public payload: DeviceConnectionState) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +113,6 @@ export type Actions
|
||||
= SetPrivilegeOnLinuxAction
|
||||
| SetPrivilegeOnLinuxReplyAction
|
||||
| ConnectionStateChangedAction
|
||||
| PermissionStateChangedAction
|
||||
| ShowSaveToKeyboardButtonAction
|
||||
| SaveConfigurationAction
|
||||
| SaveConfigurationReplyAction
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Action } from '@ngrx/store';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { NotifierService } from 'angular-notifier';
|
||||
@@ -13,16 +13,22 @@ import 'rxjs/add/operator/catch';
|
||||
import { AppStartInfo, LogService, Notification, NotificationType } from 'uhk-common';
|
||||
import {
|
||||
ActionTypes,
|
||||
ApplyCommandLineArgsAction,
|
||||
AppStartedAction,
|
||||
DismissUndoNotificationAction,
|
||||
ProcessAppStartInfoAction,
|
||||
ShowNotificationAction,
|
||||
ToggleAddonMenuAction,
|
||||
UndoLastAction, UpdateAgentVersionInformationAction
|
||||
UndoLastAction,
|
||||
UpdateAgentVersionInformationAction
|
||||
} from '../actions/app';
|
||||
import { AppRendererService } from '../../services/app-renderer.service';
|
||||
import { AppUpdateRendererService } from '../../services/app-update-renderer.service';
|
||||
import { ConnectionStateChangedAction, PermissionStateChangedAction } from '../actions/device';
|
||||
import {
|
||||
ActionTypes as DeviceActions,
|
||||
ConnectionStateChangedAction,
|
||||
SaveToKeyboardSuccessAction
|
||||
} from '../actions/device';
|
||||
import { AppState, autoWriteUserConfiguration } from '../index';
|
||||
|
||||
@Injectable()
|
||||
export class ApplicationEffects {
|
||||
@@ -56,9 +62,11 @@ export class ApplicationEffects {
|
||||
.mergeMap((appInfo: AppStartInfo) => {
|
||||
this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo);
|
||||
return [
|
||||
new ToggleAddonMenuAction(appInfo.commandLineArgs.addons),
|
||||
new ConnectionStateChangedAction(appInfo.deviceConnected),
|
||||
new PermissionStateChangedAction(appInfo.hasPermission),
|
||||
new ApplyCommandLineArgsAction(appInfo.commandLineArgs),
|
||||
new ConnectionStateChangedAction({
|
||||
connected: appInfo.deviceConnected,
|
||||
hasPermission: appInfo.hasPermission
|
||||
}),
|
||||
new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo)
|
||||
];
|
||||
});
|
||||
@@ -68,10 +76,20 @@ 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();
|
||||
}
|
||||
});
|
||||
|
||||
constructor(private actions$: Actions,
|
||||
private notifierService: NotifierService,
|
||||
private appUpdateRendererService: AppUpdateRendererService,
|
||||
private appRendererService: AppRendererService,
|
||||
private logService: LogService) {
|
||||
private logService: LogService,
|
||||
private store: Store<AppState>) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
import { Actions, Effect, toPayload } from '@ngrx/effects';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import 'rxjs/add/observable/of';
|
||||
@@ -13,13 +13,13 @@ import 'rxjs/add/operator/mergeMap';
|
||||
import 'rxjs/add/operator/withLatestFrom';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
|
||||
import { IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common';
|
||||
import { DeviceConnectionState, IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common';
|
||||
import {
|
||||
ActionTypes,
|
||||
ConnectionStateChangedAction,
|
||||
HideSaveToKeyboardButton,
|
||||
PermissionStateChangedAction,
|
||||
SaveConfigurationAction,
|
||||
SaveConfigurationReplyAction,
|
||||
SaveToKeyboardSuccessAction,
|
||||
SaveToKeyboardSuccessFailed,
|
||||
SetPrivilegeOnLinuxReplyAction,
|
||||
@@ -46,35 +46,25 @@ export class DeviceEffects {
|
||||
deviceConnectionStateChange$: Observable<Action> = this.actions$
|
||||
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
|
||||
.map(action => action.payload)
|
||||
.do((connected: boolean) => {
|
||||
if (connected) {
|
||||
.do((state: DeviceConnectionState) => {
|
||||
if (!state.hasPermission) {
|
||||
this.router.navigate(['/privilege']);
|
||||
}
|
||||
else if (state.connected) {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
else {
|
||||
this.router.navigate(['/detection']);
|
||||
}
|
||||
})
|
||||
.switchMap((connected: boolean) => {
|
||||
if (connected) {
|
||||
.switchMap((state: DeviceConnectionState) => {
|
||||
if (state.connected && state.hasPermission) {
|
||||
return Observable.of(new LoadConfigFromDeviceAction());
|
||||
}
|
||||
|
||||
return Observable.empty();
|
||||
});
|
||||
|
||||
@Effect({dispatch: false})
|
||||
permissionStateChange$: Observable<boolean> = this.actions$
|
||||
.ofType<PermissionStateChangedAction>(ActionTypes.PERMISSION_STATE_CHANGED)
|
||||
.map(action => action.payload)
|
||||
.do((hasPermission: boolean) => {
|
||||
if (hasPermission) {
|
||||
this.router.navigate(['/detection']);
|
||||
}
|
||||
else {
|
||||
this.router.navigate(['/privilege']);
|
||||
}
|
||||
});
|
||||
|
||||
@Effect({dispatch: false})
|
||||
setPrivilegeOnLinux$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX)
|
||||
@@ -89,14 +79,16 @@ export class DeviceEffects {
|
||||
.mergeMap((response: any) => {
|
||||
if (response.success) {
|
||||
return [
|
||||
new ConnectionStateChangedAction(true),
|
||||
new PermissionStateChangedAction(true)
|
||||
new ConnectionStateChangedAction({
|
||||
connected: true,
|
||||
hasPermission: true
|
||||
})
|
||||
];
|
||||
}
|
||||
return [
|
||||
<any>new ShowNotificationAction({
|
||||
type: NotificationType.Error,
|
||||
message: response.error.message
|
||||
message: response.error.message || response.error
|
||||
})
|
||||
];
|
||||
});
|
||||
@@ -113,8 +105,8 @@ export class DeviceEffects {
|
||||
|
||||
@Effect()
|
||||
saveConfigurationReply$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.SAVE_CONFIGURATION_REPLY)
|
||||
.map(toPayload)
|
||||
.ofType<SaveConfigurationReplyAction>(ActionTypes.SAVE_CONFIGURATION_REPLY)
|
||||
.map(action => action.payload)
|
||||
.mergeMap((response: IpcResponse) => {
|
||||
if (response.success) {
|
||||
return [
|
||||
|
||||
@@ -12,35 +12,27 @@ import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/operator/withLatestFrom';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
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, LoadConfigFromDeviceReplyAction, LoadUserConfigSuccessAction, RenameUserConfigurationAction,
|
||||
SaveUserConfigSuccessAction
|
||||
} from '../actions/user-config';
|
||||
|
||||
import { DataStorageRepositoryService } from '../../services/datastorage-repository.service';
|
||||
import { DefaultUserConfigurationService } from '../../services/default-user-configuration.service';
|
||||
import { AppState, getPrevUserConfiguration, getUserConfiguration } from '../index';
|
||||
import { AppState, autoWriteUserConfiguration, getPrevUserConfiguration, getUserConfiguration } from '../index';
|
||||
import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions';
|
||||
import {
|
||||
DismissUndoNotificationAction,
|
||||
LoadHardwareConfigurationSuccessAction,
|
||||
ShowNotificationAction,
|
||||
DismissUndoNotificationAction, LoadHardwareConfigurationSuccessAction, ShowNotificationAction,
|
||||
UndoLastAction
|
||||
} from '../actions/app';
|
||||
import { ShowSaveToKeyboardButtonAction } from '../actions/device';
|
||||
import { SaveConfigurationAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
|
||||
import { DeviceRendererService } from '../../services/device-renderer.service';
|
||||
import { UndoUserConfigData } from '../../models/undo-user-config-data';
|
||||
|
||||
@@ -193,6 +185,19 @@ 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();
|
||||
}
|
||||
});
|
||||
|
||||
constructor(private actions$: Actions,
|
||||
private dataStorageRepository: DataStorageRepositoryService,
|
||||
private store: Store<AppState>,
|
||||
|
||||
@@ -43,6 +43,7 @@ export const getDeviceName = (state: AppState) => state.userConfiguration.device
|
||||
|
||||
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);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { KeyboardLayout } from '../../keyboard/keyboard-layout.enum';
|
||||
export interface State {
|
||||
started: boolean;
|
||||
showAddonMenu: boolean;
|
||||
autoWriteUserConfiguration: boolean;
|
||||
undoableNotification?: Notification;
|
||||
navigationCountAfterNotification: number;
|
||||
prevUserConfig?: UserConfiguration;
|
||||
@@ -23,6 +24,7 @@ export interface State {
|
||||
export const initialState: State = {
|
||||
started: false,
|
||||
showAddonMenu: false,
|
||||
autoWriteUserConfiguration: false,
|
||||
navigationCountAfterNotification: 0,
|
||||
runningInElectron: runInElectron(),
|
||||
configLoading: true
|
||||
@@ -37,10 +39,11 @@ export function reducer(state = initialState, action: Action & { payload: any })
|
||||
};
|
||||
}
|
||||
|
||||
case ActionTypes.APP_TOGGLE_ADDON_MENU: {
|
||||
case ActionTypes.APPLY_COMMAND_LINE_ARGS: {
|
||||
return {
|
||||
...state,
|
||||
showAddonMenu: action.payload
|
||||
showAddonMenu: action.payload.addons,
|
||||
autoWriteUserConfiguration: action.payload.autoWriteConfig
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,6 +127,7 @@ export function reducer(state = initialState, action: Action & { payload: any })
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Action } from '@ngrx/store';
|
||||
import {
|
||||
ActionTypes,
|
||||
ConnectionStateChangedAction,
|
||||
PermissionStateChangedAction,
|
||||
SaveConfigurationAction, UpdateFirmwareFailedAction
|
||||
SaveConfigurationAction,
|
||||
UpdateFirmwareFailedAction
|
||||
} from '../actions/device';
|
||||
import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app';
|
||||
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
|
||||
@@ -30,17 +30,14 @@ export const initialState: State = {
|
||||
|
||||
export function reducer(state = initialState, action: Action) {
|
||||
switch (action.type) {
|
||||
case ActionTypes.CONNECTION_STATE_CHANGED:
|
||||
case ActionTypes.CONNECTION_STATE_CHANGED: {
|
||||
const data = (<ConnectionStateChangedAction>action).payload;
|
||||
return {
|
||||
...state,
|
||||
connected: (<ConnectionStateChangedAction>action).payload
|
||||
};
|
||||
|
||||
case ActionTypes.PERMISSION_STATE_CHANGED:
|
||||
return {
|
||||
...state,
|
||||
hasPermission: (<PermissionStateChangedAction>action).payload
|
||||
connected: data.connected,
|
||||
hasPermission: data.hasPermission
|
||||
};
|
||||
}
|
||||
|
||||
case ActionTypes.SAVING_CONFIGURATION: {
|
||||
return {
|
||||
|
||||
14
packages/usb/get-module-state.js
Executable file
14
packages/usb/get-module-state.js
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env node
|
||||
const uhk = require('./uhk');
|
||||
const device = uhk.getUhkDevice();
|
||||
|
||||
function getModuleState() {
|
||||
const payload = new Buffer([uhk.usbCommands.getModuleProperties, 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();
|
||||
@@ -76,6 +76,7 @@ exports = module.exports = moduleExports = {
|
||||
getDebugBuffer : 0x0b,
|
||||
getAdcValue : 0x0c,
|
||||
setLedPwmBrightness : 0x0d,
|
||||
getModuleProperties : 0x0e,
|
||||
},
|
||||
enumerationModes: {
|
||||
bootloader: 0,
|
||||
|
||||
12
packages/usb/update-loop-getkey.sh
Executable file
12
packages/usb/update-loop-getkey.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
firmwarePath=$1
|
||||
|
||||
while true; do
|
||||
read -n 1 char
|
||||
if [[ $char == "a" ]]; then
|
||||
./update-firmwares-and-configs.js "$firmwarePath" ansi
|
||||
elif [[ $char == "i" ]]; then
|
||||
./update-firmwares-and-configs.js "$firmwarePath" iso
|
||||
fi
|
||||
done
|
||||
Reference in New Issue
Block a user