Skip to content
This repository was archived by the owner on Dec 15, 2024. It is now read-only.
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: 2 additions & 0 deletions packages/uhk-usb/src/uhk-operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ export class UhkOperations {
await this.applyConfiguration();
this.logService.usb('[DeviceOperation] USB[T]: Write user configuration to EEPROM');
await this.writeConfigToEeprom(ConfigBufferId.validatedUserConfig);
await this.waitUntilKeyboardBusy();
} catch (error) {
this.logService.error('[DeviceOperation] Transferring error', error);
throw error;
Expand Down Expand Up @@ -381,6 +382,7 @@ export class UhkOperations {

await this.sendConfigToKeyboard(buffer, false);
await this.writeConfigToEeprom(ConfigBufferId.hardwareConfig);
await this.waitUntilKeyboardBusy();
}

public async writeConfigToEeprom(configBufferId: ConfigBufferId): Promise<void> {
Expand Down
20 changes: 20 additions & 0 deletions packages/uhk-usb/src/utils/get-current-uhk-80-right-HID.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { devicesAsync, Device } from 'node-hid';

import { isRightHalfCommunicationDevice } from './is-right-half-communication-device.js';

export const MULTIPLE_UHK80_RIGHT_CONNECTED_ERROR_MESSAGE = 'Multiple UHK80 Right half aren\'t supported yet, so please connect only a single right half to proceed further.';

export async function getCurrenUhk80RightHID(): Promise<Device | undefined> {
const hidDevices = await devicesAsync();

const devices = hidDevices.filter(device => isRightHalfCommunicationDevice(device));

if (devices.length === 0) {
return;
}

if (devices.length === 1)
return devices[0];

throw new Error(MULTIPLE_UHK80_RIGHT_CONNECTED_ERROR_MESSAGE);
}
2 changes: 2 additions & 0 deletions packages/uhk-usb/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './find-device-by-device-identifier.js';
export * from './get-current-uhk-device-product.js';
export * from './get-current-uhk-dongle-HID.js';
export * from './get-current-uhk-80-left-HID.js';
export * from './get-current-uhk-80-right-HID.js';
export * from './get-device-enumerate-vid-pid-pairs.js';
export * from './get-device-firmware-path.js';
export * from './get-device-user-config-path.js';
Expand All @@ -19,6 +20,7 @@ export * from './get-uhk-devices.js';
export * from './get-uhk-dongles.js';
export * from './is-dongle-communication-device.js';
export * from './is-left-half-communication-device.js';
export * from './is-right-half-communication-device.js';
export * from './is-serial-port-in-vid-pids.js';
export * from './is-uhk-device-connected.js';
export * from './is-uhk-keyboard-connected.js';
Expand Down
12 changes: 12 additions & 0 deletions packages/uhk-usb/src/utils/is-right-half-communication-device.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Device } from 'node-hid';
import { UHK_80_DEVICE } from 'uhk-common';

import { isUhkCommunicationUsage } from '../util.js';

export function isRightHalfCommunicationDevice(device: Device): boolean {
return UHK_80_DEVICE.keyboard.some(vidPid => {
return vidPid.vid === device.vendorId
&& vidPid.pid == device.productId
&& isUhkCommunicationUsage(device);
});
}
4 changes: 2 additions & 2 deletions packages/uhk-usb/src/utils/is-uhk-device-connected.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { devices as HidDevices } from 'node-hid';
import { devicesAsync } from 'node-hid';
import {SerialPort} from 'serialport';
import { UhkDeviceProduct } from 'uhk-common';

import { isUhkCommunicationUsage } from '../util.js';

export async function isUhkDeviceConnected(uhkDevice: UhkDeviceProduct): Promise<boolean> {
const hidDevices = HidDevices();
const hidDevices = await devicesAsync();

for (const device of hidDevices) {
if ((uhkDevice.keyboard.some(vidPid => vidPid.vid === device.vendorId && vidPid.pid === device.productId)
Expand Down
191 changes: 191 additions & 0 deletions packages/usb/factory-update-uhk80.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning

import fs from 'fs';
import { Device } from 'node-hid';
import path from 'node:path';
import {
FirmwareJson,
UHK_80_DEVICE,
UHK_80_DEVICE_LEFT,
UhkDeviceProduct,
} from 'uhk-common';
import {
getCurrenUhk80LeftHID,
getCurrenUhk80RightHID,
getDeviceUserConfigPath,
getDeviceFirmwarePath,
getFirmwarePackageJson,
TmpFirmware,
snooze,
UhkHidDevice,
UhkOperations,
UsbVariables,
waitForUhkDeviceConnected,
} from 'uhk-usb';

import Uhk, { errorHandler, yargs } from './src/index.js';

const argv = yargs
.scriptName('./factory-update-uhk80.ts')
.usage('Usage: $0 <firmwarePath> {iso|ansi}')
.demandCommand(2, 'Firmware path and layout are required')
.option('set-serial-number', {
description: 'Use the given serial number instead of randomly generated one.',
type: 'number',
})
.argv as any;

const firmwarePath = argv._[0] as string;
const layout = argv._[1] as string;

const { logger, rootDir } = Uhk(argv);

try {
let connectedDevices = await getHidDevices();

const firmwareDirectoryInfo: TmpFirmware = {
packageJsonPath: path.join(firmwarePath, 'package.json'),
tmpDirectory: firmwarePath,
};

const firmwarePackageJson = await getFirmwarePackageJson(firmwareDirectoryInfo);

const firmwareUpgradesResults = await Promise.all([
upgradeFirmware(UHK_80_DEVICE, connectedDevices.rightHidDevice, firmwarePackageJson),
upgradeFirmware(UHK_80_DEVICE_LEFT, connectedDevices.leftHidDevice, firmwarePackageJson),
]);

assertOperationResults(firmwareUpgradesResults, 'Firmware upgrade failed: ');

// just wait until devices be ready. After the reenumeration the halves start to communicate with each other
// give them some time to finish
await snooze(5000);

// Need to reload hid devices because after the reenumeration maybe the HID device path changed
connectedDevices = await getHidDevices();

const writeHardwareConfigResults = await Promise.all([
writeHardwareConfig(UHK_80_DEVICE, connectedDevices.rightHidDevice, layout),
writeHardwareConfig(UHK_80_DEVICE_LEFT, connectedDevices.leftHidDevice, layout),
]);

assertOperationResults(writeHardwareConfigResults, 'Hardware configuration write failed: ');

await snooze(5000); // just wait until devices be ready

// re-read hid devices just to be sure
connectedDevices = await getHidDevices();

let leftUhkDevice: UhkHidDevice;
let rightUhkDevice: UhkHidDevice;
try {
leftUhkDevice = new UhkHidDevice(logger, {}, rootDir, connectedDevices.leftHidDevice);
rightUhkDevice = new UhkHidDevice(logger, {}, rootDir, connectedDevices.rightHidDevice);
const rightUhkOperations = new UhkOperations(logger, rightUhkDevice);
const leftUhkOperations = new UhkOperations(logger, leftUhkDevice);

// Save user-config to the right half
const userConfigPath = getDeviceUserConfigPath(UHK_80_DEVICE, firmwarePackageJson);
const configBuffer = fs.readFileSync(userConfigPath) as any;
await rightUhkOperations.saveUserConfiguration(configBuffer);

// pair left and right half
await rightUhkOperations.pairToLeftHalf(leftUhkDevice);

await rightUhkOperations.setVariable(UsbVariables.testSwitches, 1);
await leftUhkOperations.setVariable(UsbVariables.testSwitches, 1);
}
finally {
leftUhkDevice?.close();
rightUhkDevice?.close();
}

} catch (error) {
await errorHandler(error);
}

interface OperationResult {
uhkDeviceProduct: UhkDeviceProduct;
error?: any;
}

async function getHidDevices() {
const rightHidDevice = await getCurrenUhk80RightHID();
if (!rightHidDevice) {
console.error('Cannot find UHK 80 right half!');
process.exit(1);
}

const leftHidDevice = await getCurrenUhk80LeftHID();
if (!leftHidDevice) {
console.error('Cannot find UHK 80 left half!');
process.exit(1);
}

return {
rightHidDevice,
leftHidDevice,
};
}

function assertOperationResults(operationResults: OperationResult[], errorMessage: string) {
let hasError = false;
for (const operationResult of operationResults) {
if (operationResult.error) {
hasError = true;
console.error(errorMessage, operationResult.uhkDeviceProduct.logName);
console.error(operationResult.error);
console.error('\n');
}
}

if (hasError) {
process.exit(1);
}
}

async function upgradeFirmware(uhkDeviceProduct: UhkDeviceProduct, hidDevice: Device, packageJson: FirmwareJson): Promise<OperationResult> {
const result: OperationResult = {
uhkDeviceProduct
};

let uhkHidDevice: UhkHidDevice;
try {
const deviceFirmwarePath = getDeviceFirmwarePath(uhkDeviceProduct, packageJson);
uhkHidDevice = new UhkHidDevice(logger, {}, rootDir, hidDevice);
const uhkOperations = new UhkOperations(logger, uhkHidDevice);
await uhkOperations.updateDeviceFirmware(deviceFirmwarePath, uhkDeviceProduct);
await waitForUhkDeviceConnected(uhkDeviceProduct);
}
catch (error) {
result.error = error;
}
finally {
uhkHidDevice?.close();
}

return result;
}

async function writeHardwareConfig(uhkDeviceProduct: UhkDeviceProduct, hidDevice: Device, layout: string ): Promise<OperationResult> {
const result: OperationResult = {
uhkDeviceProduct
};

let uhkHidDevice: UhkHidDevice;

try {
uhkHidDevice = new UhkHidDevice(logger, {}, rootDir, hidDevice);
const uhkOperations = new UhkOperations(logger, uhkHidDevice);
await uhkOperations.saveHardwareConfiguration(layout === 'iso', uhkDeviceProduct.id, argv.setSerialNumber);
}
catch (error) {
result.error = error;
}
finally {
uhkHidDevice?.close();
}


return result;
}