25 Commits

Author SHA1 Message Date
László Monda
721a4dc6e7 Bump Agent version to 1.0.2 and update changelog. 2017-12-25 23:55:23 +01:00
László Monda
c9f03b4e57 Reference firmware 8.0.1 and don't include the moduleProtocolVersion field because it's firmware-related, not Agent-related. 2017-12-25 23:39:27 +01:00
László Monda
bbb6839f7e Fix Linux blhost path. 2017-12-25 21:48:41 +01:00
László Monda
dd973c80ea Add install-win-driver-*.bat 2017-12-24 03:56:13 +01:00
László Monda
48574a121a Move zadic-*.exe to packages/zadic 2017-12-24 03:35:13 +01:00
László Monda
8bc2462589 Reluctantly, add back zadic as it's needed to install Windows drivers for blhost to update the firmware on Windows. 2017-12-24 03:12:49 +01:00
László Monda
975ab8a5e9 Update README.md 2017-12-23 19:24:13 +01:00
László Monda
f0a54768d4 Clarify the difference between the two builds. 2017-12-23 19:20:26 +01:00
Róbert Kiss
0b69d01a0d style: add missing semicolon (#526)
* fix(updater): check updater error is exists or not

* set default error message

* style: add missing semicolon

* style: add missing semicolon
2017-12-22 21:38:44 +01:00
László Monda
838a92836c Update changelog. 2017-12-22 20:06:44 +01:00
Róbert Kiss
500ccc296b fix(device): use tmp path to run setup-rules.sh (#523) 2017-12-22 19:58:54 +01:00
Róbert Kiss
8bb9f7f839 fix(updater): handle undefined error when updater not provide error content (#524)
* fix(updater): check updater error is exists or not

* set default error message
2017-12-22 19:50:31 +01:00
László Monda
5d1cc9202b Bump version to 1.0.1 and update changelog. 2017-12-21 21:21:06 +01:00
Róbert Kiss
c11658f1fc fix(device): check privilege on Linux (#519)
* fix(device): check privilege on Linux

* device connected if also have permission

* fix rules sh path

* refactor permission detection

* fic hasPermission condition

* fix return value
2017-12-21 20:33:40 +01:00
László Monda
969c36561b Fix script name. 2017-12-15 04:33:10 +01:00
László Monda
04904aca5d Merge branch 'auto-write-config' 2017-12-15 04:27:18 +01:00
László Monda
ef0b0aa4ba Add mass-updater script. 2017-12-15 04:25:28 +01:00
László Monda
3967593c9c Add get-module-state.js and the relevant device command id. 2017-12-15 02:58:51 +01:00
László Monda
b8be1c965b Fix typo: blink-led-pwm-brithness.js -> blink-led-pwm-brightness.js 2017-12-15 02:49:36 +01:00
Róbert Kiss
b32c93f0f8 feat(agent): automatically write user configuration after app started (#516)
* auto write userconfig

* fix load success action

* exit from app

* add electron:auto-write-config script
2017-12-14 23:36:43 +01:00
Róbert Kiss
711d3c0690 add electron:auto-write-config script 2017-12-14 23:22:13 +01:00
Róbert Kiss
42b4465230 exit from app 2017-12-14 23:19:34 +01:00
Róbert Kiss
2bf7d545a2 fix load success action 2017-12-14 22:50:24 +01:00
Róbert Kiss
d09c46af42 Merge branch 'master' into auto-write-config 2017-12-14 22:01:12 +01:00
Róbert Kiss
d57ba81d10 auto write userconfig 2017-12-14 21:59:44 +01:00
37 changed files with 251 additions and 126 deletions

View File

@@ -4,6 +4,18 @@ 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/). The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
## [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
- 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
- Fix Linux privilege escalation when udev rules aren't set up.
## [1.0.0] - 2017-12-14 ## [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](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

View File

@@ -7,7 +7,17 @@ Agent is the configuration application of the [Ultimate Hacking Keyboard](https:
[Give it a whirl!](http://ultimatehackingkeyboard.github.io/agent/) [Give it a whirl!](http://ultimatehackingkeyboard.github.io/agent/)
## Set up instructions ## Two builds to rule them all
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 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: First up, make sure that node >=8.1.x and npm >=5.1.x are installed on your system. Next up:
@@ -24,6 +34,13 @@ npm run electron
At this point, Agent should be running on your machine. At this point, Agent should be running on your machine.
## Developing the web application
- The frontend code is located in `packages/uhk-web/`
- Run the project locally with `npm run server:web`
- View the app at `http://localhost:8080`
- The app will automatically reload when you make changes
## Contributing ## Contributing
Wanna contribute? Please let us show you [how](CONTRIBUTING.md). Wanna contribute? Please let us show you [how](CONTRIBUTING.md).

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "uhk-agent", "name": "uhk-agent",
"version": "1.0.0", "version": "1.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -3,7 +3,7 @@
"private": true, "private": true,
"author": "Ultimate Gadget Laboratories", "author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js", "main": "electron/dist/electron-main.js",
"version": "1.0.0", "version": "1.0.2",
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.", "description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -86,6 +86,7 @@
"server:web": "lerna exec --scope uhk-web npm start", "server:web": "lerna exec --scope uhk-web npm start",
"server:electron": "lerna exec --scope uhk-web npm run server:renderer", "server:electron": "lerna exec --scope uhk-web npm run server:renderer",
"electron": "lerna exec --scope uhk-agent npm start", "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", "standard-version": "standard-version",
"pack": "node ./scripts/release.js", "pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites", "sprites": "node ./scripts/generate-svg-sprites",

View File

@@ -36,6 +36,7 @@
}, },
"scripts": { "scripts": {
"start": "electron ./dist/electron-main.js", "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": "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", "build:usb": "electron-rebuild -w node-hid -p -m ./dist",
"install:build-deps": "cd ./dist && npm i", "install:build-deps": "cd ./dist && npm i",

View File

@@ -10,7 +10,7 @@ import * as url from 'url';
import * as commandLineArgs from 'command-line-args'; import * as commandLineArgs from 'command-line-args';
import { UhkHidDevice, UhkOperations } from 'uhk-usb'; import { UhkHidDevice, UhkOperations } from 'uhk-usb';
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service'; // 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 { DeviceService } from './services/device.service';
import { logger } from './services/logger.service'; import { logger } from './services/logger.service';
import { AppUpdateService } from './services/app-update.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 { SudoService } from './services/sudo.service';
import { UhkBlhost } from '../../uhk-usb/src'; import { UhkBlhost } from '../../uhk-usb/src';
import * as isDev from 'electron-is-dev'; import * as isDev from 'electron-is-dev';
import { CommandLineInputs } from './models/command-line-inputs';
const optionDefinitions = [ 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'; // import './dev-extension';
// require('electron-debug')({ showDevTools: true, enabled: true }); // require('electron-debug')({ showDevTools: true, enabled: true });

View File

@@ -0,0 +1,4 @@
export interface CommandLineInputs {
addons?: boolean;
'auto-write-config'?: boolean;
}

View File

@@ -16,9 +16,8 @@
"dependencies": { "dependencies": {
"node-hid": "0.5.7" "node-hid": "0.5.7"
}, },
"firmwareVersion": "7.0.0", "firmwareVersion": "8.0.1",
"deviceProtocolVersion": "4.0.0", "deviceProtocolVersion": "4.0.0",
"moduleProtocolVersion": "3.0.0",
"userConfigVersion": "4.0.0", "userConfigVersion": "4.0.0",
"hardwareConfigVersion": "1.0.0" "hardwareConfigVersion": "1.0.0"
} }

View File

@@ -37,8 +37,13 @@ export class AppUpdateService extends MainServiceBase {
}); });
autoUpdater.on('error', (ev: any, err: string) => { autoUpdater.on('error', (ev: any, err: string) => {
console.error('[AppUpdateService] error', err); this.logService.error('[AppUpdateService] error', err);
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateError, err.substr(0, 100)); let msg = 'Electron updater error';
if (err) {
msg = err.substr(0, 100);
}
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateError, msg);
}); });
autoUpdater.on('download-progress', (progressObj: ProgressInfo) => { autoUpdater.on('download-progress', (progressObj: ProgressInfo) => {

View File

@@ -3,19 +3,21 @@ import { UhkHidDevice } from 'uhk-usb';
import { readFile } from 'fs'; import { readFile } from 'fs';
import { join } from 'path'; 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 { MainServiceBase } from './main-service-base';
import { DeviceService } from './device.service'; import { DeviceService } from './device.service';
import { CommandLineInputs } from '../models/command-line-inputs';
export class AppService extends MainServiceBase { export class AppService extends MainServiceBase {
constructor(protected logService: LogService, constructor(protected logService: LogService,
protected win: Electron.BrowserWindow, protected win: Electron.BrowserWindow,
private deviceService: DeviceService, private deviceService: DeviceService,
private options: CommandLineArgs, private options: CommandLineInputs,
private uhkHidDeviceService: UhkHidDevice) { private uhkHidDeviceService: UhkHidDevice) {
super(logService, win); super(logService, win);
ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this)); ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this));
ipcMain.on(IpcEvents.app.exit, this.exit.bind(this));
logService.info('[AppService] init success'); logService.info('[AppService] init success');
} }
@@ -25,8 +27,11 @@ export class AppService extends MainServiceBase {
const packageJson = await this.getPackageJson(); const packageJson = await this.getPackageJson();
const response: AppStartInfo = { const response: AppStartInfo = {
commandLineArgs: this.options, commandLineArgs: {
deviceConnected: this.deviceService.isConnected, addons: this.options.addons || false,
autoWriteConfig: this.options['auto-write-config'] || false
},
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
hasPermission: this.uhkHidDeviceService.hasPermission(), hasPermission: this.uhkHidDeviceService.hasPermission(),
agentVersionInfo: { agentVersionInfo: {
version: packageJson.version, version: packageJson.version,
@@ -57,4 +62,9 @@ export class AppService extends MainServiceBase {
}); });
}); });
} }
private exit() {
this.logService.info('[AppService] exit');
this.win.close();
}
} }

View File

@@ -1,9 +1,8 @@
import { ipcMain } from 'electron'; import { ipcMain } from 'electron';
import { ConfigurationReply, IpcEvents, IpcResponse, LogService } from 'uhk-common'; import { ConfigurationReply, DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
import { Constants, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb'; import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs/Subscription';
import { Device, devices } from 'node-hid';
import { emptyDir } from 'fs-extra'; import { emptyDir } from 'fs-extra';
import 'rxjs/add/observable/interval'; import 'rxjs/add/observable/interval';
@@ -19,13 +18,11 @@ import { QueueManager } from './queue-manager';
/** /**
* IpcMain pair of the UHK Communication * IpcMain pair of the UHK Communication
* Functionality: * Functionality:
* - Detect device is connected or not
* - Send UserConfiguration to the UHK Device * - Send UserConfiguration to the UHK Device
* - Read UserConfiguration from the UHK Device * - Read UserConfiguration from the UHK Device
*/ */
export class DeviceService { export class DeviceService {
private pollTimer$: Subscription; private pollTimer$: Subscription;
private connected = false;
private queueManager = new QueueManager(); private queueManager = new QueueManager();
constructor(private logService: LogService, constructor(private logService: LogService,
@@ -66,14 +63,6 @@ export class DeviceService {
logService.debug('[DeviceService] init success'); 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 * Return with the actual UserConfiguration from UHK Device
* @returns {Promise<Buffer>} * @returns {Promise<Buffer>}
@@ -89,7 +78,6 @@ export class DeviceService {
success: true, success: true,
...result ...result
}; };
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
} catch (error) { } catch (error) {
response = { response = {
success: false, success: false,
@@ -103,7 +91,6 @@ export class DeviceService {
} }
public close(): void { public close(): void {
this.connected = false;
this.stopPollTimer(); this.stopPollTimer();
this.logService.info('[DeviceService] Device connection checker stopped.'); this.logService.info('[DeviceService] Device connection checker stopped.');
} }
@@ -154,15 +141,16 @@ export class DeviceService {
this.pollTimer$ = Observable.interval(1000) this.pollTimer$ = Observable.interval(1000)
.startWith(0) .startWith(0)
.map(() => { .map(() => this.device.deviceConnected())
return devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID &&
dev.productId === Constants.PRODUCT_ID);
})
.distinctUntilChanged() .distinctUntilChanged()
.do((connected: boolean) => { .do((connected: boolean) => {
this.connected = connected; const response: DeviceConnectionState = {
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, connected); connected,
this.logService.info(`[DeviceService] Device connection state changed to: ${connected}`); hasPermission: this.device.hasPermission()
};
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
this.logService.info('[DeviceService] Device connection state changed to:', response);
}) })
.subscribe(); .subscribe();
} }

View File

@@ -2,6 +2,8 @@ import { ipcMain, app } from 'electron';
import * as isDev from 'electron-is-dev'; import * as isDev from 'electron-is-dev';
import * as path from 'path'; import * as path from 'path';
import * as sudo from 'sudo-prompt'; import * as sudo from 'sudo-prompt';
import { dirSync } from 'tmp';
import { emptyDir, copy } from 'fs-extra';
import { IpcEvents, LogService, IpcResponse } from 'uhk-common'; import { IpcEvents, LogService, IpcResponse } from 'uhk-common';
@@ -10,7 +12,7 @@ export class SudoService {
constructor(private logService: LogService) { constructor(private logService: LogService) {
if (isDev) { 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 { } else {
this.rootDir = path.dirname(app.getAppPath()); this.rootDir = path.dirname(app.getAppPath());
} }
@@ -18,10 +20,10 @@ export class SudoService {
ipcMain.on(IpcEvents.device.setPrivilegeOnLinux, this.setPrivilege.bind(this)); ipcMain.on(IpcEvents.device.setPrivilegeOnLinux, this.setPrivilege.bind(this));
} }
private setPrivilege(event: Electron.Event) { private async setPrivilege(event: Electron.Event) {
switch (process.platform) { switch (process.platform) {
case 'linux': case 'linux':
this.setPrivilegeOnLinux(event); await this.setPrivilegeOnLinux(event);
break; break;
default: default:
const response: IpcResponse = { const response: IpcResponse = {
@@ -34,23 +36,31 @@ export class SudoService {
} }
} }
private setPrivilegeOnLinux(event: Electron.Event) { private async setPrivilegeOnLinux(event: Electron.Event) {
const scriptPath = path.join(this.rootDir, 'rules/setup-rules.sh'); 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 = { const options = {
name: 'Setting UHK access rules' name: 'Setting UHK access rules'
}; };
const command = `sh ${scriptPath}`; const command = `sh ${scriptPath}`;
console.log(command); this.logService.debug('[SudoService] Set privilege command: ', command);
sudo.exec(command, options, (error: any) => { sudo.exec(command, options, async (error: any) => {
const response = new IpcResponse(); const response = new IpcResponse();
if (error) { if (error) {
this.logService.error('[SudoService] Error when set privilege: ', error);
response.success = false; response.success = false;
response.error = error; response.error = error;
} else { } else {
response.success = true; response.success = true;
} }
await emptyDir(tmpDirectory.name);
event.sender.send(IpcEvents.device.setPrivilegeOnLinuxReply, response); event.sender.send(IpcEvents.device.setPrivilegeOnLinuxReply, response);
}); });
} }

View File

@@ -1,3 +1,4 @@
export interface CommandLineArgs { export interface CommandLineArgs {
addons: boolean; addons: boolean;
autoWriteConfig: boolean;
} }

View File

@@ -0,0 +1,4 @@
export interface DeviceConnectionState {
connected: boolean;
hasPermission: boolean;
}

View File

@@ -4,3 +4,4 @@ export * from './ipc-response';
export * from './app-start-info'; export * from './app-start-info';
export * from './configuration-reply'; export * from './configuration-reply';
export * from './version-information'; export * from './version-information';
export * from './device-connection-state';

View File

@@ -2,6 +2,7 @@ class App {
public static readonly appStarted = 'app-started'; public static readonly appStarted = 'app-started';
public static readonly getAppStartInfo = 'app-get-start-info'; public static readonly getAppStartInfo = 'app-get-start-info';
public static readonly getAppStartInfoReply = 'app-get-start-info-reply'; public static readonly getAppStartInfoReply = 'app-get-start-info-reply';
public static readonly exit = 'app-exit';
} }
class AutoUpdate { class AutoUpdate {

View File

@@ -21,7 +21,8 @@ export enum UsbCommand {
SetTestLed = 0x0a, SetTestLed = 0x0a,
GetDebugBuffer = 0x0b, GetDebugBuffer = 0x0b,
GetAdcValue = 0x0c, GetAdcValue = 0x0c,
SetLedPwmBrightness = 0x0d SetLedPwmBrightness = 0x0d,
GetModuleProperties = 0x0e
} }
export enum EepromOperation { export enum EepromOperation {

View File

@@ -70,7 +70,7 @@ export class UhkBlhost {
let blhostPath; let blhostPath;
switch (process.platform) { switch (process.platform) {
case 'linux': case 'linux':
blhostPath = 'linux/amd64/blhost'; blhostPath = 'linux/x86_64/blhost';
break; break;
case 'darwin': case 'darwin':
blhostPath = 'mac/blhost'; blhostPath = 'mac/blhost';

View File

@@ -25,6 +25,7 @@ export class UhkHidDevice {
* @private * @private
*/ */
private _device: HID; private _device: HID;
private _hasPermission = false;
constructor(private logService: LogService) { constructor(private logService: LogService) {
} }
@@ -38,8 +39,18 @@ export class UhkHidDevice {
*/ */
public hasPermission(): boolean { public hasPermission(): boolean {
try { try {
devices(); if (this._hasPermission) {
return true; return true;
}
if (!this.deviceConnected()) {
return true;
}
this._hasPermission = this.getDevice() !== null;
this.close();
return this._hasPermission;
} catch (err) { } catch (err) {
this.logService.error('[UhkHidDevice] hasPermission', err); this.logService.error('[UhkHidDevice] hasPermission', err);
} }
@@ -47,6 +58,21 @@ export class UhkHidDevice {
return false; 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. * Send data to the UHK device and wait for the response.
* Throw an error when 1st byte of the response is not 0 * Throw an error when 1st byte of the response is not 0
@@ -225,7 +251,7 @@ export class UhkHidDevice {
} }
} }
function kbootKommandName(module: ModuleSlotToI2cAddress): string { function kbootKommandName(module: ModuleSlotToI2cAddress): string {
switch (module) { switch (module) {
case ModuleSlotToI2cAddress.leftHalf: case ModuleSlotToI2cAddress.leftHalf:
return 'leftHalf'; return 'leftHalf';

View File

@@ -21,6 +21,11 @@ export class AppRendererService {
this.ipcRenderer.send(IpcEvents.app.getAppStartInfo); this.ipcRenderer.send(IpcEvents.app.getAppStartInfo);
} }
exit() {
this.logService.info('[AppRendererService] exit');
this.ipcRenderer.send(IpcEvents.app.exit);
}
private registerEvents() { private registerEvents() {
this.ipcRenderer.on(IpcEvents.app.getAppStartInfoReply, (event: string, arg: AppStartInfo) => { this.ipcRenderer.on(IpcEvents.app.getAppStartInfoReply, (event: string, arg: AppStartInfo) => {
this.dispachStoreAction(new ProcessAppStartInfoAction(arg)); this.dispachStoreAction(new ProcessAppStartInfoAction(arg));

View File

@@ -1,7 +1,7 @@
import { Injectable, NgZone } from '@angular/core'; import { Injectable, NgZone } from '@angular/core';
import { Action, Store } from '@ngrx/store'; 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 { AppState } from '../store';
import { IpcCommonRenderer } from './ipc-common-renderer'; import { IpcCommonRenderer } from './ipc-common-renderer';
import { import {
@@ -47,7 +47,7 @@ export class DeviceRendererService {
} }
private registerEvents(): void { 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)); this.dispachStoreAction(new ConnectionStateChangedAction(arg));
}); });

View File

@@ -1,6 +1,6 @@
import { Action } from '@ngrx/store'; 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'; import { ElectronLogEntry } from '../../models/xterm-log';
const PREFIX = '[app] '; const PREFIX = '[app] ';
@@ -10,7 +10,7 @@ export const ActionTypes = {
APP_BOOTSRAPPED: type(PREFIX + 'bootstrapped'), APP_BOOTSRAPPED: type(PREFIX + 'bootstrapped'),
APP_STARTED: type(PREFIX + 'started'), APP_STARTED: type(PREFIX + 'started'),
APP_SHOW_NOTIFICATION: type(PREFIX + 'show notification'), 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'), APP_PROCESS_START_INFO: type(PREFIX + 'process start info'),
UNDO_LAST: type(PREFIX + 'undo last action'), UNDO_LAST: type(PREFIX + 'undo last action'),
UNDO_LAST_SUCCESS: type(PREFIX + 'undo last action success'), UNDO_LAST_SUCCESS: type(PREFIX + 'undo last action success'),
@@ -34,10 +34,10 @@ export class ShowNotificationAction implements Action {
constructor(public payload: Notification) { } constructor(public payload: Notification) { }
} }
export class ToggleAddonMenuAction implements Action { export class ApplyCommandLineArgsAction implements Action {
type = ActionTypes.APP_TOGGLE_ADDON_MENU; type = ActionTypes.APPLY_COMMAND_LINE_ARGS;
constructor(public payload: boolean) { } constructor(public payload: CommandLineArgs) { }
} }
export class ProcessAppStartInfoAction implements Action { export class ProcessAppStartInfoAction implements Action {
@@ -82,7 +82,7 @@ export type Actions
= AppStartedAction = AppStartedAction
| AppBootsrappedAction | AppBootsrappedAction
| ShowNotificationAction | ShowNotificationAction
| ToggleAddonMenuAction | ApplyCommandLineArgsAction
| ProcessAppStartInfoAction | ProcessAppStartInfoAction
| UndoLastAction | UndoLastAction
| UndoLastSuccessAction | UndoLastSuccessAction

View File

@@ -1,5 +1,5 @@
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { IpcResponse, type } from 'uhk-common'; import { DeviceConnectionState, IpcResponse, type } from 'uhk-common';
const PREFIX = '[device] '; const PREFIX = '[device] ';
@@ -8,7 +8,6 @@ export const ActionTypes = {
SET_PRIVILEGE_ON_LINUX: type(PREFIX + 'set privilege on linux'), SET_PRIVILEGE_ON_LINUX: type(PREFIX + 'set privilege on linux'),
SET_PRIVILEGE_ON_LINUX_REPLY: type(PREFIX + 'set privilege on linux reply'), SET_PRIVILEGE_ON_LINUX_REPLY: type(PREFIX + 'set privilege on linux reply'),
CONNECTION_STATE_CHANGED: type(PREFIX + 'connection state changed'), CONNECTION_STATE_CHANGED: type(PREFIX + 'connection state changed'),
PERMISSION_STATE_CHANGED: type(PREFIX + 'permission state changed'),
SAVE_CONFIGURATION: type(PREFIX + 'save configuration'), SAVE_CONFIGURATION: type(PREFIX + 'save configuration'),
SAVE_CONFIGURATION_REPLY: type(PREFIX + 'save configuration reply'), SAVE_CONFIGURATION_REPLY: type(PREFIX + 'save configuration reply'),
SAVING_CONFIGURATION: type(PREFIX + 'saving configuration'), SAVING_CONFIGURATION: type(PREFIX + 'saving configuration'),
@@ -39,14 +38,7 @@ export class SetPrivilegeOnLinuxReplyAction implements Action {
export class ConnectionStateChangedAction implements Action { export class ConnectionStateChangedAction implements Action {
type = ActionTypes.CONNECTION_STATE_CHANGED; type = ActionTypes.CONNECTION_STATE_CHANGED;
constructor(public payload: boolean) { constructor(public payload: DeviceConnectionState) {
}
}
export class PermissionStateChangedAction implements Action {
type = ActionTypes.PERMISSION_STATE_CHANGED;
constructor(public payload: boolean) {
} }
} }
@@ -121,7 +113,6 @@ export type Actions
= SetPrivilegeOnLinuxAction = SetPrivilegeOnLinuxAction
| SetPrivilegeOnLinuxReplyAction | SetPrivilegeOnLinuxReplyAction
| ConnectionStateChangedAction | ConnectionStateChangedAction
| PermissionStateChangedAction
| ShowSaveToKeyboardButtonAction | ShowSaveToKeyboardButtonAction
| SaveConfigurationAction | SaveConfigurationAction
| SaveConfigurationReplyAction | SaveConfigurationReplyAction

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store'; import { Action, Store } from '@ngrx/store';
import { Actions, Effect } from '@ngrx/effects'; import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { NotifierService } from 'angular-notifier'; import { NotifierService } from 'angular-notifier';
@@ -13,16 +13,22 @@ import 'rxjs/add/operator/catch';
import { AppStartInfo, LogService, Notification, NotificationType } from 'uhk-common'; import { AppStartInfo, LogService, Notification, NotificationType } from 'uhk-common';
import { import {
ActionTypes, ActionTypes,
ApplyCommandLineArgsAction,
AppStartedAction, AppStartedAction,
DismissUndoNotificationAction, DismissUndoNotificationAction,
ProcessAppStartInfoAction, ProcessAppStartInfoAction,
ShowNotificationAction, ShowNotificationAction,
ToggleAddonMenuAction, UndoLastAction,
UndoLastAction, UpdateAgentVersionInformationAction UpdateAgentVersionInformationAction
} from '../actions/app'; } from '../actions/app';
import { AppRendererService } from '../../services/app-renderer.service'; import { AppRendererService } from '../../services/app-renderer.service';
import { AppUpdateRendererService } from '../../services/app-update-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() @Injectable()
export class ApplicationEffects { export class ApplicationEffects {
@@ -56,9 +62,11 @@ export class ApplicationEffects {
.mergeMap((appInfo: AppStartInfo) => { .mergeMap((appInfo: AppStartInfo) => {
this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo); this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo);
return [ return [
new ToggleAddonMenuAction(appInfo.commandLineArgs.addons), new ApplyCommandLineArgsAction(appInfo.commandLineArgs),
new ConnectionStateChangedAction(appInfo.deviceConnected), new ConnectionStateChangedAction({
new PermissionStateChangedAction(appInfo.hasPermission), connected: appInfo.deviceConnected,
hasPermission: appInfo.hasPermission
}),
new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo) new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo)
]; ];
}); });
@@ -68,10 +76,20 @@ export class ApplicationEffects {
.map(action => action.payload) .map(action => action.payload)
.mergeMap((action: Action) => [action, new DismissUndoNotificationAction()]); .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, constructor(private actions$: Actions,
private notifierService: NotifierService, private notifierService: NotifierService,
private appUpdateRendererService: AppUpdateRendererService, private appUpdateRendererService: AppUpdateRendererService,
private appRendererService: AppRendererService, private appRendererService: AppRendererService,
private logService: LogService) { private logService: LogService,
private store: Store<AppState>) {
} }
} }

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Action, Store } from '@ngrx/store'; 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 { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of'; import 'rxjs/add/observable/of';
@@ -13,13 +13,13 @@ import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/withLatestFrom'; import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/switchMap'; import 'rxjs/add/operator/switchMap';
import { IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common'; import { DeviceConnectionState, IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common';
import { import {
ActionTypes, ActionTypes,
ConnectionStateChangedAction, ConnectionStateChangedAction,
HideSaveToKeyboardButton, HideSaveToKeyboardButton,
PermissionStateChangedAction,
SaveConfigurationAction, SaveConfigurationAction,
SaveConfigurationReplyAction,
SaveToKeyboardSuccessAction, SaveToKeyboardSuccessAction,
SaveToKeyboardSuccessFailed, SaveToKeyboardSuccessFailed,
SetPrivilegeOnLinuxReplyAction, SetPrivilegeOnLinuxReplyAction,
@@ -46,35 +46,25 @@ export class DeviceEffects {
deviceConnectionStateChange$: Observable<Action> = this.actions$ deviceConnectionStateChange$: Observable<Action> = this.actions$
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED) .ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
.map(action => action.payload) .map(action => action.payload)
.do((connected: boolean) => { .do((state: DeviceConnectionState) => {
if (connected) { if (!state.hasPermission) {
this.router.navigate(['/privilege']);
}
else if (state.connected) {
this.router.navigate(['/']); this.router.navigate(['/']);
} }
else { else {
this.router.navigate(['/detection']); this.router.navigate(['/detection']);
} }
}) })
.switchMap((connected: boolean) => { .switchMap((state: DeviceConnectionState) => {
if (connected) { if (state.connected && state.hasPermission) {
return Observable.of(new LoadConfigFromDeviceAction()); return Observable.of(new LoadConfigFromDeviceAction());
} }
return Observable.empty(); 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}) @Effect({dispatch: false})
setPrivilegeOnLinux$: Observable<Action> = this.actions$ setPrivilegeOnLinux$: Observable<Action> = this.actions$
.ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX) .ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX)
@@ -89,14 +79,16 @@ export class DeviceEffects {
.mergeMap((response: any) => { .mergeMap((response: any) => {
if (response.success) { if (response.success) {
return [ return [
new ConnectionStateChangedAction(true), new ConnectionStateChangedAction({
new PermissionStateChangedAction(true) connected: true,
hasPermission: true
})
]; ];
} }
return [ return [
<any>new ShowNotificationAction({ <any>new ShowNotificationAction({
type: NotificationType.Error, type: NotificationType.Error,
message: response.error.message message: response.error.message || response.error
}) })
]; ];
}); });
@@ -113,8 +105,8 @@ export class DeviceEffects {
@Effect() @Effect()
saveConfigurationReply$: Observable<Action> = this.actions$ saveConfigurationReply$: Observable<Action> = this.actions$
.ofType(ActionTypes.SAVE_CONFIGURATION_REPLY) .ofType<SaveConfigurationReplyAction>(ActionTypes.SAVE_CONFIGURATION_REPLY)
.map(toPayload) .map(action => action.payload)
.mergeMap((response: IpcResponse) => { .mergeMap((response: IpcResponse) => {
if (response.success) { if (response.success) {
return [ return [

View File

@@ -12,35 +12,27 @@ import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/withLatestFrom'; import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/mergeMap'; import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/observable/of'; import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import { import {
ConfigurationReply, ConfigurationReply, HardwareConfiguration, LogService, NotificationType, UhkBuffer,
HardwareConfiguration,
LogService,
NotificationType,
UhkBuffer,
UserConfiguration UserConfiguration
} from 'uhk-common'; } from 'uhk-common';
import { import {
ActionTypes, ActionTypes, LoadConfigFromDeviceReplyAction, LoadUserConfigSuccessAction, RenameUserConfigurationAction,
LoadConfigFromDeviceReplyAction,
LoadUserConfigSuccessAction,
RenameUserConfigurationAction,
SaveUserConfigSuccessAction SaveUserConfigSuccessAction
} from '../actions/user-config'; } from '../actions/user-config';
import { DataStorageRepositoryService } from '../../services/datastorage-repository.service'; import { DataStorageRepositoryService } from '../../services/datastorage-repository.service';
import { DefaultUserConfigurationService } from '../../services/default-user-configuration.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 { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions';
import { import {
DismissUndoNotificationAction, DismissUndoNotificationAction, LoadHardwareConfigurationSuccessAction, ShowNotificationAction,
LoadHardwareConfigurationSuccessAction,
ShowNotificationAction,
UndoLastAction UndoLastAction
} from '../actions/app'; } from '../actions/app';
import { ShowSaveToKeyboardButtonAction } from '../actions/device'; import { SaveConfigurationAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
import { DeviceRendererService } from '../../services/device-renderer.service'; import { DeviceRendererService } from '../../services/device-renderer.service';
import { UndoUserConfigData } from '../../models/undo-user-config-data'; import { UndoUserConfigData } from '../../models/undo-user-config-data';
@@ -193,6 +185,19 @@ export class UserConfigEffects {
saveAs(blob, 'UserConfiguration.bin'); 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, constructor(private actions$: Actions,
private dataStorageRepository: DataStorageRepositoryService, private dataStorageRepository: DataStorageRepositoryService,
private store: Store<AppState>, private store: Store<AppState>,

View File

@@ -43,6 +43,7 @@ export const getDeviceName = (state: AppState) => state.userConfiguration.device
export const appState = (state: AppState) => state.app; export const appState = (state: AppState) => state.app;
export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu); export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu);
export const autoWriteUserConfiguration = createSelector(appState, fromApp.autoWriteUserConfiguration);
export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification); export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification);
export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration); export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration);
export const runningInElectron = createSelector(appState, fromApp.runningInElectron); export const runningInElectron = createSelector(appState, fromApp.runningInElectron);

View File

@@ -11,6 +11,7 @@ import { KeyboardLayout } from '../../keyboard/keyboard-layout.enum';
export interface State { export interface State {
started: boolean; started: boolean;
showAddonMenu: boolean; showAddonMenu: boolean;
autoWriteUserConfiguration: boolean;
undoableNotification?: Notification; undoableNotification?: Notification;
navigationCountAfterNotification: number; navigationCountAfterNotification: number;
prevUserConfig?: UserConfiguration; prevUserConfig?: UserConfiguration;
@@ -23,6 +24,7 @@ export interface State {
export const initialState: State = { export const initialState: State = {
started: false, started: false,
showAddonMenu: false, showAddonMenu: false,
autoWriteUserConfiguration: false,
navigationCountAfterNotification: 0, navigationCountAfterNotification: 0,
runningInElectron: runInElectron(), runningInElectron: runInElectron(),
configLoading: true 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 { return {
...state, ...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 showAddonMenu = (state: State) => state.showAddonMenu;
export const autoWriteUserConfiguration = (state: State) => state.autoWriteUserConfiguration;
export const getUndoableNotification = (state: State) => state.undoableNotification; export const getUndoableNotification = (state: State) => state.undoableNotification;
export const getPrevUserConfiguration = (state: State) => state.prevUserConfig; export const getPrevUserConfiguration = (state: State) => state.prevUserConfig;
export const runningInElectron = (state: State) => state.runningInElectron; export const runningInElectron = (state: State) => state.runningInElectron;

View File

@@ -3,8 +3,8 @@ import { Action } from '@ngrx/store';
import { import {
ActionTypes, ActionTypes,
ConnectionStateChangedAction, ConnectionStateChangedAction,
PermissionStateChangedAction, SaveConfigurationAction,
SaveConfigurationAction, UpdateFirmwareFailedAction UpdateFirmwareFailedAction
} from '../actions/device'; } from '../actions/device';
import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app'; import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app';
import { initProgressButtonState, ProgressButtonState } from './progress-button-state'; import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
@@ -30,17 +30,14 @@ export const initialState: State = {
export function reducer(state = initialState, action: Action) { export function reducer(state = initialState, action: Action) {
switch (action.type) { switch (action.type) {
case ActionTypes.CONNECTION_STATE_CHANGED: case ActionTypes.CONNECTION_STATE_CHANGED: {
const data = (<ConnectionStateChangedAction>action).payload;
return { return {
...state, ...state,
connected: (<ConnectionStateChangedAction>action).payload connected: data.connected,
}; hasPermission: data.hasPermission
case ActionTypes.PERMISSION_STATE_CHANGED:
return {
...state,
hasPermission: (<PermissionStateChangedAction>action).payload
}; };
}
case ActionTypes.SAVING_CONFIGURATION: { case ActionTypes.SAVING_CONFIGURATION: {
return { return {

View 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();

View File

@@ -76,6 +76,7 @@ exports = module.exports = moduleExports = {
getDebugBuffer : 0x0b, getDebugBuffer : 0x0b,
getAdcValue : 0x0c, getAdcValue : 0x0c,
setLedPwmBrightness : 0x0d, setLedPwmBrightness : 0x0d,
getModuleProperties : 0x0e,
}, },
enumerationModes: { enumerationModes: {
bootloader: 0, bootloader: 0,

View 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

View File

@@ -0,0 +1 @@
zadic-ia32.exe --vid 0x1d50 --pid 0x6120 --iface 0 --usealldevices --noprompt

View File

@@ -0,0 +1 @@
zadic-x64.exe --vid 0x1d50 --pid 0x6120 --iface 0 --usealldevices --noprompt

Binary file not shown.

Binary file not shown.