From 3f7a69618633c5e419fe61623bb5cf592cbc76a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiss=20R=C3=B3bert?= Date: Sat, 30 Nov 2024 21:43:26 +0100 Subject: [PATCH] chore: factory-update-uhk80.ts usb script --- packages/uhk-usb/src/uhk-operations.ts | 2 + .../src/utils/get-current-uhk-80-right-HID.ts | 20 ++ packages/uhk-usb/src/utils/index.ts | 2 + .../is-right-half-communication-device.ts | 12 ++ .../src/utils/is-uhk-device-connected.ts | 4 +- packages/usb/factory-update-uhk80.ts | 191 ++++++++++++++++++ 6 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 packages/uhk-usb/src/utils/get-current-uhk-80-right-HID.ts create mode 100644 packages/uhk-usb/src/utils/is-right-half-communication-device.ts create mode 100755 packages/usb/factory-update-uhk80.ts diff --git a/packages/uhk-usb/src/uhk-operations.ts b/packages/uhk-usb/src/uhk-operations.ts index 34cb5aae..0ad5ea02 100644 --- a/packages/uhk-usb/src/uhk-operations.ts +++ b/packages/uhk-usb/src/uhk-operations.ts @@ -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; @@ -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 { diff --git a/packages/uhk-usb/src/utils/get-current-uhk-80-right-HID.ts b/packages/uhk-usb/src/utils/get-current-uhk-80-right-HID.ts new file mode 100644 index 00000000..a051d115 --- /dev/null +++ b/packages/uhk-usb/src/utils/get-current-uhk-80-right-HID.ts @@ -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 { + 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); +} diff --git a/packages/uhk-usb/src/utils/index.ts b/packages/uhk-usb/src/utils/index.ts index b134d275..4d60d849 100644 --- a/packages/uhk-usb/src/utils/index.ts +++ b/packages/uhk-usb/src/utils/index.ts @@ -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'; @@ -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'; diff --git a/packages/uhk-usb/src/utils/is-right-half-communication-device.ts b/packages/uhk-usb/src/utils/is-right-half-communication-device.ts new file mode 100644 index 00000000..0cb40f2f --- /dev/null +++ b/packages/uhk-usb/src/utils/is-right-half-communication-device.ts @@ -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); + }); +} diff --git a/packages/uhk-usb/src/utils/is-uhk-device-connected.ts b/packages/uhk-usb/src/utils/is-uhk-device-connected.ts index 6117b5cb..7931847a 100644 --- a/packages/uhk-usb/src/utils/is-uhk-device-connected.ts +++ b/packages/uhk-usb/src/utils/is-uhk-device-connected.ts @@ -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 { - 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) diff --git a/packages/usb/factory-update-uhk80.ts b/packages/usb/factory-update-uhk80.ts new file mode 100755 index 00000000..013903f6 --- /dev/null +++ b/packages/usb/factory-update-uhk80.ts @@ -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 {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 { + 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 { + 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; +}