Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/test-serializer/spec/test-serializer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { UhkBuffer, UserConfiguration } from '../../uhk-common/src/index.js';

import fs from 'fs';

const userConfig = JSON.parse(fs.readFileSync('../uhk-web/src/app/services/user-config-80.json', { encoding: 'utf8' }));
const userConfig = JSON.parse(fs.readFileSync('../uhk-common/user-config-80.json', { encoding: 'utf8' }));

describe('Test Serializer', () => {
it('full config match', () => {
Expand Down
29 changes: 27 additions & 2 deletions packages/uhk-agent/src/electron-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@ import { SudoService } from './services/sudo.service';
import { SmartMacroDocService } from './services/smart-macro-doc.service';
import isDev from 'electron-is-dev';
import { setMenu } from './electron-menu';
import { printUsbDevices } from './util';
import { loadWindowState, saveWindowState } from './util/window';
import { getWindowBackgroundColor, options, cliUsage, reenumerateAndExit } from './util';
import {
getWindowBackgroundColor,
options,
cliUsage,
printStatusBuffer,
printUsbDevices,
printHardwareConfiguration,
reenumerateAndExit,
restoreUserConfiguration,
writeHardwareConfiguration,
} from './util';

if (options.help) {
console.log(cliUsage);
Expand Down Expand Up @@ -162,6 +171,10 @@ async function createWindow() {

if (isSecondInstance) {
app.quit();
} else if (options['print-hardware-configuration']) {
printHardwareConfiguration({ logger, uhkOperations })
} else if (options['print-status-buffer']) {
printStatusBuffer({ logger, uhkOperations })
} else if (options['print-usb-devices']) {
printUsbDevices()
.then(() => {
Expand All @@ -187,6 +200,18 @@ if (isSecondInstance) {
logger.misc('Reenumeration process finished with error. Please unplug and plug your UHK.');
process.exit(-1);
});
} else if (options['restore-user-configuration']) {
restoreUserConfiguration({
logger,
uhkOperations,
commandLineArgs: options,
})
} else if (options['write-hardware-configuration']) {
writeHardwareConfiguration({
logger,
uhkOperations,
commandLineArgs: options,
})
} else {

// This method will be called when Electron has finished
Expand Down
26 changes: 25 additions & 1 deletion packages/uhk-agent/src/util/command-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ const optionDefinitions: commandLineArgs.OptionDefinition[] = [
{ name: 'pid', type: Number },
{ name: 'no-report-id', type: Boolean },
{ name: 'preserve-udev-rules', type: Boolean },
{ name: 'print-hardware-configuration', type: Boolean },
{ name: 'print-status-buffer', type: Boolean },
{ name: 'print-usb-devices', type: Boolean },
{ name: 'restore-user-configuration', type: Boolean },
{ name: 'reenumerate-and-exit', type: String },
{ name: 'report-id', type: Number },
{ name: 'serial-number', type: String },
{ name: 'spe', type: Boolean }, // simulate privilege escalation error
{ name: 'usb-interface', type: Number },
{ name: 'usb-non-blocking', type: Boolean },
{ name: 'vid', type: Number },
{ name: 'write-hardware-configuration', type: String },
];

export const options: CommandLineArgs = commandLineArgs(optionDefinitions, { partial: true }) as CommandLineArgs;
Expand Down Expand Up @@ -74,6 +78,16 @@ const sections: commandLineUsage.Section[] = [
description: 'Don\'t force udev rule update',
type: Boolean
},
{
name: 'print-hardware-configuration',
description: 'Print hardware configuration to the standard output and exit.',
type: Boolean
},
{
name: 'print-status-buffer',
description: 'Print the status buffer of the keyboard to the standard output and exit.',
type: Boolean
},
{
name: 'print-usb-devices',
description: 'Print usb devices to the standard output and exit.',
Expand All @@ -86,6 +100,11 @@ const sections: commandLineUsage.Section[] = [
'Please provide the timeout in milliseconds.',
typeLabel: '(bootloader|buspal),timeout'
},
{
name: 'restore-user-configuration',
description: 'Run restore user-configuration process and exit.',
type: Boolean,
},
{
name: 'report-id',
description: 'Report Id that used for USB communication. If the value is -1 then does not use report id. The default value depends from the UHK device. For UHK 60 is 0. For UHK 80 is 4',
Expand Down Expand Up @@ -114,7 +133,12 @@ const sections: commandLineUsage.Section[] = [
name: 'vid',
description: 'Use the specified USB vendor id. If you set it you have to set the pid too.',
type: Number
}
},
{
name: 'write-hardware-configuration',
description: 'Overwrite/reset the current hardware configuration and exit.',
typeLabel: 'ansi | iso'
},
]
}
];
Expand Down
4 changes: 4 additions & 0 deletions packages/uhk-agent/src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export * from './get-window-background-color';
export * from './load-user-config-from-binary-file';
export * from './load-user-config-history-async';
export * from './make-folder-writeable-to-user-on-linux';
export * from './print-hardware-configuration';
export * from './print-status-buffer';
export * from './print-usb-devices';
export * from './reenumerate-and-exit';
export * from './restore-user-configuration';
export * from './save-extract-firmware';
export * from './save-user-config-history-async';
export * from './write-hardware-configuration';
21 changes: 21 additions & 0 deletions packages/uhk-agent/src/util/print-hardware-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import process from 'node:process';
import { UhkOperations } from 'uhk-usb';

import { ElectronLogService } from '../services/logger.service';

export interface PrintHardwareConfigurationOptions {
logger: ElectronLogService;
uhkOperations: UhkOperations;
}

export async function printHardwareConfiguration({logger, uhkOperations}: PrintHardwareConfigurationOptions): Promise<void> {
try {
const hardwareConfiguration = await uhkOperations.getHardwareConfiguration()
logger.misc(hardwareConfiguration.toJsonObject());
process.exit(0);
}
catch (error) {
logger.error(error.message);
process.exit(-1);
}
}
21 changes: 21 additions & 0 deletions packages/uhk-agent/src/util/print-status-buffer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import process from 'node:process';
import { UhkOperations, UsbVariables } from 'uhk-usb';

import { ElectronLogService } from '../services/logger.service';

export interface PrintStatusBufferOptions {
logger: ElectronLogService;
uhkOperations: UhkOperations;
}

export async function printStatusBuffer({logger, uhkOperations}: PrintStatusBufferOptions): Promise<void> {
try {
const message = await uhkOperations.getVariable(UsbVariables.statusBuffer);
logger.misc(`Status buffer: ${message}`);
process.exit(0);
}
catch (error) {
logger.error(error.message);
process.exit(-1);
}
}
53 changes: 53 additions & 0 deletions packages/uhk-agent/src/util/restore-user-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import process from 'node:process';
import {
CommandLineArgs,
mapObjectToUserConfigBinaryBuffer,
UHK_60_DEVICE,
UHK_60_V2_DEVICE,
UHK_60_USER_CONFIG,
UHK_80_DEVICE,
UHK_80_USER_CONFIG,
} from 'uhk-common';
import { getCurrentUhkDeviceProduct, UhkOperations } from 'uhk-usb';

import { ElectronLogService } from '../services/logger.service';

export interface RestoreUserConfigurationOptions {
logger: ElectronLogService;
uhkOperations: UhkOperations;
commandLineArgs: CommandLineArgs;
}

export async function restoreUserConfiguration(options: RestoreUserConfigurationOptions): Promise<void> {
try {
const device = await getCurrentUhkDeviceProduct(options.commandLineArgs);
let userConfigJson: any;

if (!device) {
options.logger.error('Cannot detect UHK device');
process.exit(-1);
}
else if (device.id === UHK_60_DEVICE.id || device.id === UHK_60_V2_DEVICE.id) {
userConfigJson = UHK_60_USER_CONFIG;
}
else if (device.id === UHK_80_DEVICE.id) {
userConfigJson = UHK_80_USER_CONFIG;
}
else {
options.logger.error(`Unknow UHK device: ${JSON.stringify(device)}`);
process.exit(-1);
}

const buffer = mapObjectToUserConfigBinaryBuffer(userConfigJson);

options.logger.misc('Start restoring user configuration...');
await options.uhkOperations.saveUserConfiguration(buffer)
options.logger.misc('User configuration restored.');

process.exit(0);
}
catch (error) {
options.logger.error(error.message);
process.exit(-1);
}
}
37 changes: 37 additions & 0 deletions packages/uhk-agent/src/util/write-hardware-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import process from 'node:process';
import { CommandLineArgs } from 'uhk-common';
import {
getCurrentUhkDeviceProduct,
UhkOperations,
} from 'uhk-usb';

import { ElectronLogService } from '../services/logger.service';

export interface WriteHardwareConfigurationOptions {
commandLineArgs: CommandLineArgs;
logger: ElectronLogService;
uhkOperations: UhkOperations;
}

export async function writeHardwareConfiguration(options: WriteHardwareConfigurationOptions):Promise<void> {
const layout = options.commandLineArgs['write-hardware-configuration'];
options.logger.misc(`[writeHardwareConfiguration] Command line argument: ${layout}`);

if (!['ansi', 'iso'].includes(layout)) {
options.logger.misc('Invalid layout. Layout should be either iso or ansi');
process.exit(-1);
}

try {
const device = await getCurrentUhkDeviceProduct(options.commandLineArgs);

await options.uhkOperations.saveHardwareConfiguration(layout === 'iso', device.id)

options.logger.misc(`[writeHardwareConfiguration] finished successfully.`);
process.exit(0);
} catch (error) {
options.logger.error(error.message);
process.exit(-1);
}

}
3 changes: 3 additions & 0 deletions packages/uhk-common/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.nyc_output/
coverage/
user-config.json
src/user-config-60.ts
src/user-config-80.ts
src/util/versions.ts
5 changes: 3 additions & 2 deletions packages/uhk-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
"url": "[email protected]:UltimateHackingKeyboard/agent.git"
},
"scripts": {
"build": "run-s -sn build:generate-versions build:tsc",
"build": "run-s -sn build:generate-versions build:user-config build:tsc",
"build:generate-versions": "node ./scripts/generate-versions.mjs",
"build:tsc": "tsc --project src/tsconfig.build.json",
"clean": "rimraf ./node_modules ./dist",
"build:user-config": "tsx ./scripts/generate-user-configs.ts",
"clean": "rimraf ./node_modules ./dist user-config.json",
"test": "cross-env NODE_OPTIONS=--loader=ts-node/esm jasmine --config=jasmine.json",
"coverage": "nyc npm test",
"lint": "eslint"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import {
UHK_60_LEFT_MAX_KEY_ACTION_COUNT,
UHK_60_RIGHT_MAX_KEY_ACTION_COUNT,
UserConfiguration,
} from "uhk-common";
} from '../src/config-serializer';

const uhk80UserConfigPath = path.join(import.meta.dirname, '../src/app/services/user-config-80.json');
const uhk80UserConfigPath = path.join(import.meta.dirname, '../user-config-80.json');
const uhk80UserConfigJson = JSON.parse(await fs.readFile(uhk80UserConfigPath, { encoding: 'utf8' }));
const uhk60UserConfig = new UserConfiguration().fromJsonObject(uhk80UserConfigJson);
const uhk80UserConfigTsPath = path.join(import.meta.dirname, '../src/user-config-80.ts');
const uhk80UserConfigContent = `export const UHK_80_USER_CONFIG = ${JSON.stringify(uhk80UserConfigJson, null, 2)}`
await fs.writeFile(uhk80UserConfigTsPath, uhk80UserConfigContent, { encoding: 'utf8' });

for (const keymap of uhk60UserConfig.keymaps) {
const layers = [];
Expand All @@ -35,5 +38,9 @@ for (const keymap of uhk60UserConfig.keymaps) {
keymap.layers = layers;
}

const uhk60UserConfigPath = path.join(import.meta.dirname, '../src/app/services/user-config.json');
const uhk60UserConfigPath = path.join(import.meta.dirname, '../user-config.json');
await fs.writeFile(uhk60UserConfigPath, JSON.stringify(uhk60UserConfig.toJsonObject(), null, 2), { encoding: 'utf8' });

const uhk60UserConfigTsPath = path.join(import.meta.dirname, '../src/user-config-60.ts');
const uhk60UserConfigContent = `export const UHK_60_USER_CONFIG = ${JSON.stringify(uhk60UserConfig.toJsonObject(), null, 2)}`
await fs.writeFile(uhk60UserConfigTsPath, uhk60UserConfigContent, { encoding: 'utf8' });
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
KeyActionHelper,
Module,
UserConfiguration,
} from "uhk-common";
} from '../dist/index.js';

const sourceFile = process.argv[2]
const destinationFile = process.argv[3]
Expand Down
4 changes: 2 additions & 2 deletions packages/uhk-common/src/config-serializer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Given that the development dependencies are installed on your system you should

There are 3 different representations of the configuration, each filling a specific purpose.

The **JavaScript representation** is optimally suited to be serialized as JSON, and saved to the file system, or transmitted over the network. As a plaintext format, it's human-readable and easily editable. See [user-config.json](../../../uhk-web/src/app/services/user-config-80.json) for an example configuration.
The **JavaScript representation** is optimally suited to be serialized as JSON, and saved to the file system, or transmitted over the network. As a plaintext format, it's human-readable and easily editable. See [user-config.json](../../user-config-80.json) for an example configuration.

The **TypeScript representation** is structurally similar to the JavaScript representation, but it features strongly typed TypeScript objects instead of typeless JavaScript objects. This representation is meant to be used by Agent. Extensive, per-property [assertion](assert.ts) takes place upon initializing the TypeScript objects to ensure the integrity of the configuration.

Expand Down Expand Up @@ -75,7 +75,7 @@ KeyActions.toJsObject: <KeyActions length="9">

## Testing the serializer

[test-serializer.ts](test-serializer.ts) is designed to test the serializer by taking [user-config.json](../../../uhk-web/src/app/services/user-config.-80json), and transforming it to TypeScript representation, then to binary representation, then finally back to JavaScript representation. This should exercise every major code path.
[test-serializer.ts](test-serializer.ts) is designed to test the serializer by taking [user-config.json](../../user-config-80.json), and transforming it to TypeScript representation, then to binary representation, then finally back to JavaScript representation. This should exercise every major code path.

If the testing is successful the following should be displayed:

Expand Down
2 changes: 2 additions & 0 deletions packages/uhk-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ export { Buffer } from './buffer.js';
export * from './config-serializer/index.js';
export * from './log/index.js';
export * from './models/index.js';
export { UHK_60_USER_CONFIG } from './user-config-60.js';
export { UHK_80_USER_CONFIG } from './user-config-80.js';
export * from './util/index.js';
export const RIGHT_HALF_FIRMWARE_UPGRADE_MODULE_NAME = 'Right keyboard half';
20 changes: 17 additions & 3 deletions packages/uhk-common/src/models/command-line-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,23 @@ export interface CommandLineArgs extends DeviceIdentifier {
* Agent not force the udev rule update
*/
'preserve-udev-rules'?: boolean;
/**
* Agent not force the udev rule update
*/

'print-hardware-configuration'?: boolean;

'print-status-buffer'?: boolean;

'print-usb-devices'?: boolean;
/**
* Reenumerate as the bootloader or BusPal, wait for the specified timeout and exit.
* This may make Windows install the USB drivers needed for firmware update.
*/
'reenumerate-and-exit'?: string;

/**
* Run restore user-configuration process and exit.
*/
'restore-user-configuration'?: boolean;

/**
* Report Id that used for USB communication
*/
Expand All @@ -78,4 +86,10 @@ export interface CommandLineArgs extends DeviceIdentifier {
* Use USB non-blocking communication
*/
'usb-non-blocking'?: boolean;

/**
* Overwrite/reset the current hardware configuration and exit.
* The argument is the keyboard layout {ansi|iso}
*/
'write-hardware-configuration'?: string;
}
Loading
Loading