From 3d945330738cfb9fe9b5826a05df177be50f2253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20Monda?= Date: Tue, 7 Jul 2026 22:10:35 +0200 Subject: [PATCH 1/6] feat: improve firmware screen with per-device progress, cable warning, and toggleable log - Show a determinate per-device progress bar with live percentage during firmware flashing, wired from the mcumgr/kboot write operations through a new moduleFirmwareUpgradeProgress IPC event. - Surface the "connect the left/right half via USB" prompt as a prominent, easily-noticeable warning on the firmware screen via a dedicated IPC signal instead of relying on the update log. - Hide the firmware update log by default and add a Show/Hide toggle that appears once the log has any text. Implements UltimateHackingKeyboard/agent#2593. Co-authored-by: Cursor --- packages/kboot/src/kboot.ts | 4 +- packages/kboot/src/peripheral.ts | 2 +- packages/kboot/src/usb-peripheral.ts | 5 +- packages/kboot/test/test-peripheral.ts | 3 +- packages/mcumgr/src/mcumgr.ts | 4 +- .../uhk-agent/src/services/device.service.ts | 66 +++++++++++++---- .../models/firmware-upgrade-connect-prompt.ts | 3 + packages/uhk-common/src/models/index.ts | 2 + .../module-firmware-upgrade-progress.ts | 4 + packages/uhk-common/src/util/ipcEvents.ts | 2 + packages/uhk-usb/src/uhk-operations.ts | 30 +++++--- .../firmware/device-firmware.component.html | 73 +++++++++++++------ .../firmware/device-firmware.component.scss | 52 +++++++++++++ .../firmware/device-firmware.component.ts | 15 +++- .../src/app/models/firmware-upgrade-state.ts | 2 + .../app/services/device-renderer.service.ts | 12 +++ .../uhk-web/src/app/store/actions/device.ts | 20 +++++ .../reducers/firmware-upgrade.reducer.ts | 39 ++++++++++ 18 files changed, 282 insertions(+), 56 deletions(-) create mode 100644 packages/uhk-common/src/models/firmware-upgrade-connect-prompt.ts create mode 100644 packages/uhk-common/src/models/module-firmware-upgrade-progress.ts diff --git a/packages/kboot/src/kboot.ts b/packages/kboot/src/kboot.ts index 8e45df2c709..0294df62167 100644 --- a/packages/kboot/src/kboot.ts +++ b/packages/kboot/src/kboot.ts @@ -151,9 +151,9 @@ export class KBoot { return this.peripheral.readMemory(startAddress, count); } - async writeMemory(options: DataOption): Promise { + async writeMemory(options: DataOption, onProgress?: (percent: number) => void): Promise { logger('Start write memory %o', { options }); - return this.peripheral.writeMemory(options); + return this.peripheral.writeMemory(options, onProgress); } /** diff --git a/packages/kboot/src/peripheral.ts b/packages/kboot/src/peripheral.ts index 3e5def0fda3..3ec9b91fcca 100644 --- a/packages/kboot/src/peripheral.ts +++ b/packages/kboot/src/peripheral.ts @@ -7,7 +7,7 @@ export interface Peripheral { sendCommand(options: CommandOption): Promise; - writeMemory(data: DataOption): Promise; + writeMemory(data: DataOption, onProgress?: (percent: number) => void): Promise; readMemory(startAddress: number, count: number): Promise; } diff --git a/packages/kboot/src/usb-peripheral.ts b/packages/kboot/src/usb-peripheral.ts index a2f33f2f0cd..baf6e7ad09d 100644 --- a/packages/kboot/src/usb-peripheral.ts +++ b/packages/kboot/src/usb-peripheral.ts @@ -73,8 +73,10 @@ export class UsbPeripheral implements Peripheral { } } - async writeMemory(option: DataOption): Promise { + async writeMemory(option: DataOption, onProgress?: (percent: number) => void): Promise { try { + onProgress?.(0); + const command: CommandOption = { command: Commands.WriteMemory, hasDataPhase: true, @@ -110,6 +112,7 @@ export class UsbPeripheral implements Peripheral { await this._device.write(writeData); // workaround to prevent main thread blocking await snooze(1); + onProgress?.(Math.min(100, Math.round((i + slice.length) / option.data.length * 100))); } const receivedData = await this._device.read(option.timeout || 2000); diff --git a/packages/kboot/test/test-peripheral.ts b/packages/kboot/test/test-peripheral.ts index 410995afc10..d7ae9372751 100644 --- a/packages/kboot/test/test-peripheral.ts +++ b/packages/kboot/test/test-peripheral.ts @@ -17,7 +17,8 @@ export class TestPeripheral implements Peripheral { return Promise.resolve(response); } - writeMemory(data: DataOption): Promise { + writeMemory(data: DataOption, onProgress?: (percent: number) => void): Promise { + onProgress?.(100); return Promise.resolve(); } diff --git a/packages/mcumgr/src/mcumgr.ts b/packages/mcumgr/src/mcumgr.ts index 867cc0ee091..1293ae7ecb0 100644 --- a/packages/mcumgr/src/mcumgr.ts +++ b/packages/mcumgr/src/mcumgr.ts @@ -74,9 +74,10 @@ export class McuManager { /** * Upload a firmware/bootloader image to the device */ - async imageUpload(buffer: Buffer): Promise { + async imageUpload(buffer: Buffer, onProgress?: (percent: number) => void): Promise { logger('Start send image upload command: %o', { bufferLength: buffer.byteLength }); let written = 0; + onProgress?.(0); while (written < buffer.length) { const message: ImageUploadRequest = { @@ -104,6 +105,7 @@ export class McuManager { } logger('Image uploaded: %d', written / buffer.length * 100); + onProgress?.(Math.min(100, Math.round(written / buffer.length * 100))); } } diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts index a0930c95649..dd5d734ce67 100644 --- a/packages/uhk-agent/src/services/device.service.ts +++ b/packages/uhk-agent/src/services/device.service.ts @@ -16,6 +16,7 @@ import { escapeZephyrControlChars, findUhkModuleById, FIRMWARE_UPGRADE_METHODS, + FirmwareUpgradeConnectPrompt, FirmwareUpgradeIpcResponse, getHardwareConfigFromDeviceResponse, getUserConfigFromDeviceResponse, @@ -32,6 +33,7 @@ import { LeftSlotModules, LogService, mapObjectToUserConfigBinaryBuffer, + ModuleFirmwareUpgradeProgress, ModuleFirmwareUpgradeSkipInfo, ModuleFirmwareUpgradeSkipReason, ModuleInfo, @@ -515,6 +517,24 @@ export class DeviceService { let shouldEnableFirmwareVersionCheck = false; let uhkDeviceProduct: UhkDeviceProduct; + const createFirmwareProgressReporter = (moduleName: string): ((percent: number) => void) => { + let lastProgress = -1; + + return (percent: number) => { + const progress = Math.max(0, Math.min(100, Math.round(percent))); + + if (progress === lastProgress) { + return; + } + + lastProgress = progress; + event.sender.send(IpcEvents.device.moduleFirmwareUpgradeProgress, { + moduleName, + progress, + } as ModuleFirmwareUpgradeProgress); + }; + }; + try { await this.stopPollUhkDevice(); await this.dongleZephyrLogService.disable(); @@ -560,7 +580,7 @@ export class DeviceService { moduleName: UHK_DONGLE.name, newFirmwareChecksum: deviceConfig.md5, } as CurrentlyUpdatingModuleInfo); - await dongleOperations.updateDeviceFirmware(dongleFirmwarePath, UHK_DONGLE); + await dongleOperations.updateDeviceFirmware(dongleFirmwarePath, UHK_DONGLE, createFirmwareProgressReporter(UHK_DONGLE.name)); this.logService.misc('[DeviceService] Waiting for keyboard'); await waitForDevices(UHK_DONGLE.keyboard); await dongleUhkDevice.close(); @@ -609,7 +629,7 @@ export class DeviceService { newFirmwareChecksum: deviceConfig.md5, moduleName: RIGHT_HALF_FIRMWARE_UPGRADE_MODULE_NAME, } as CurrentlyUpdatingModuleInfo); - await this.operations.updateDeviceFirmware(deviceFirmwarePath, uhkDeviceProduct); + await this.operations.updateDeviceFirmware(deviceFirmwarePath, uhkDeviceProduct, createFirmwareProgressReporter(RIGHT_HALF_FIRMWARE_UPGRADE_MODULE_NAME)); this.logService.misc('[DeviceService] Waiting for keyboard'); await waitForDevices(uhkDeviceProduct.keyboard); @@ -673,27 +693,29 @@ export class DeviceService { } as CurrentlyUpdatingModuleInfo); if(uhkDeviceProduct.firmwareUpgradeMethod === FIRMWARE_UPGRADE_METHODS.MCUBOOT) { - if (!(await isUhkDeviceConnected(UHK_80_DEVICE_LEFT))) { - this.logService.misc('[DeviceService] To continue the firmware upgrade, now connect the left half via USB. (You can disconnect the right half or use a second USB cable.)'); - } + await this.waitForUhkDeviceWithConnectPrompt( + event, + UHK_80_DEVICE_LEFT, + 'To continue the firmware upgrade, now connect the left half via USB. (You can disconnect the right half or use a second USB cable.)' + ); - await waitForUhkDeviceConnected(UHK_80_DEVICE_LEFT); await snooze(1000); const firmwarePath = getDeviceFirmwarePath(UHK_80_DEVICE_LEFT, packageJson); - await this.operations.updateFirmwareWithMcuManager(firmwarePath, UHK_80_DEVICE_LEFT); - - if (!(await isUhkDeviceConnected(uhkDeviceProduct))) { - this.logService.misc('[DeviceService] To finish the firmware upgrade, now connect the right half via USB. (You can disconnect the left half or use a second USB cable.)'); - } + await this.operations.updateFirmwareWithMcuManager(firmwarePath, UHK_80_DEVICE_LEFT, createFirmwareProgressReporter(leftModuleInfo.module.name)); - await waitForUhkDeviceConnected(uhkDeviceProduct); + await this.waitForUhkDeviceWithConnectPrompt( + event, + uhkDeviceProduct, + 'To finish the firmware upgrade, now connect the right half via USB. (You can disconnect the left half or use a second USB cable.)' + ); } else { await this.operations .updateModuleWithKboot( getModuleFirmwarePath(leftModuleInfo.module, packageJson), uhkDeviceProduct, - leftModuleInfo.module + leftModuleInfo.module, + createFirmwareProgressReporter(leftModuleInfo.module.name) ); } } else { @@ -755,7 +777,8 @@ export class DeviceService { .updateModuleWithKboot( getModuleFirmwarePath(moduleInfo.module, packageJson), uhkDeviceProduct, - moduleInfo.module + moduleInfo.module, + createFirmwareProgressReporter(moduleInfo.module.name) ); this.logService.misc(`[DeviceService] "${moduleInfo.module.name}" firmware update done.`); } else { @@ -815,9 +838,24 @@ export class DeviceService { await this.dongleZephyrLogService.enable(); await this.leftHalfZephyrLogService.enable(); + event.sender.send(IpcEvents.device.firmwareUpgradeConnectPrompt, null); event.sender.send(IpcEvents.device.updateFirmwareReply, response); } + private async waitForUhkDeviceWithConnectPrompt( + event: Electron.IpcMainEvent, + device: UhkDeviceProduct, + message: string + ): Promise { + if (!(await isUhkDeviceConnected(device))) { + this.logService.misc(`[DeviceService] ${message}`); + event.sender.send(IpcEvents.device.firmwareUpgradeConnectPrompt, { message } as FirmwareUpgradeConnectPrompt); + } + + await waitForUhkDeviceConnected(device); + event.sender.send(IpcEvents.device.firmwareUpgradeConnectPrompt, null); + } + public async recoveryDevice(event: Electron.IpcMainEvent, args): Promise { const response: FirmwareUpgradeIpcResponse = { success: false, diff --git a/packages/uhk-common/src/models/firmware-upgrade-connect-prompt.ts b/packages/uhk-common/src/models/firmware-upgrade-connect-prompt.ts new file mode 100644 index 00000000000..e6a517763a8 --- /dev/null +++ b/packages/uhk-common/src/models/firmware-upgrade-connect-prompt.ts @@ -0,0 +1,3 @@ +export interface FirmwareUpgradeConnectPrompt { + message: string; +} diff --git a/packages/uhk-common/src/models/index.ts b/packages/uhk-common/src/models/index.ts index ad794f03f4d..4e52cc9aebc 100644 --- a/packages/uhk-common/src/models/index.ts +++ b/packages/uhk-common/src/models/index.ts @@ -10,6 +10,7 @@ export * from './device-version-information.js'; export * from './dongle.js'; export * from './firmware-json.js'; export * from './firmware-repo-info.js'; +export * from './firmware-upgrade-connect-prompt.js'; export * from './firmware-upgrade-method.js'; export * from './firmware-version.js'; export * from './firmware-version-info.js'; @@ -23,6 +24,7 @@ export * from './app-start-info.js'; export * from './configuration-reply.js'; export * from './version-information.js'; export * from './device-connection-state.js'; +export * from './module-firmware-upgrade-progress.js'; export * from './module-firmware-upgrade-skip-info.js'; export * from './module-slot-to-i2c-adress.js'; export * from './module-slot-id.js'; diff --git a/packages/uhk-common/src/models/module-firmware-upgrade-progress.ts b/packages/uhk-common/src/models/module-firmware-upgrade-progress.ts new file mode 100644 index 00000000000..78515af673c --- /dev/null +++ b/packages/uhk-common/src/models/module-firmware-upgrade-progress.ts @@ -0,0 +1,4 @@ +export interface ModuleFirmwareUpgradeProgress { + moduleName: string; + progress: number; +} diff --git a/packages/uhk-common/src/util/ipcEvents.ts b/packages/uhk-common/src/util/ipcEvents.ts index 6144399d7e8..0c8b05b524e 100644 --- a/packages/uhk-common/src/util/ipcEvents.ts +++ b/packages/uhk-common/src/util/ipcEvents.ts @@ -53,6 +53,8 @@ export class Device { public static readonly updateFirmware = 'device-update-firmware'; public static readonly updateFirmwareJson = 'device-update-firmware-json'; public static readonly updateFirmwareReply = 'device-update-firmware-reply'; + public static readonly firmwareUpgradeConnectPrompt = 'device-firmware-upgrade-connect-prompt'; + public static readonly moduleFirmwareUpgradeProgress = 'device-module-firmware-upgrade-progress'; public static readonly moduleFirmwareUpgradeSkip = 'device-module-firmware-upgrade-skip'; public static readonly moduleFirmwareUpgrading = 'device-module-firmware-upgrading'; public static readonly startConnectionPoller = 'device-start-connection-poller'; diff --git a/packages/uhk-usb/src/uhk-operations.ts b/packages/uhk-usb/src/uhk-operations.ts index 3ae27fd0a80..bf8d39e4e1f 100644 --- a/packages/uhk-usb/src/uhk-operations.ts +++ b/packages/uhk-usb/src/uhk-operations.ts @@ -123,22 +123,22 @@ export class UhkOperations { await this.device.write(transfer); } - public async updateDeviceFirmware(firmwarePath: string, device: UhkDeviceProduct): Promise { + public async updateDeviceFirmware(firmwarePath: string, device: UhkDeviceProduct, onProgress?: (percent: number) => void): Promise { this.logService.misc(`[UhkOperations] Start flashing device firmware with ${device.firmwareUpgradeMethod}`); switch (device.firmwareUpgradeMethod) { case FIRMWARE_UPGRADE_METHODS.KBOOT: - return this.updateRightFirmwareWithKboot(firmwarePath, device); + return this.updateRightFirmwareWithKboot(firmwarePath, device, onProgress); case FIRMWARE_UPGRADE_METHODS.MCUBOOT: - return this.updateFirmwareWithMcuManager(firmwarePath, device); + return this.updateFirmwareWithMcuManager(firmwarePath, device, onProgress); default: throw new Error(`Firmware upgrade method not implemented: ${device.firmwareUpgradeMethod}`); } } - public async updateRightFirmwareWithKboot(firmwarePath: string, device: UhkDeviceProduct): Promise { + public async updateRightFirmwareWithKboot(firmwarePath: string, device: UhkDeviceProduct, onProgress?: (percent: number) => void): Promise { if (!(await existsAsync(firmwarePath))) { throw new Error(`Firmware path not found: ${firmwarePath}`); } @@ -162,13 +162,20 @@ export class UhkOperations { this.logService.misc('[UhkOperations] Read RIGHT firmware from file'); const bootloaderMemoryMap = await readBootloaderFirmwareFromHexFileAsync(firmwarePath); this.logService.misc('[UhkOperations] Write memory'); + const totalBytes = [...bootloaderMemoryMap.values()].reduce((sum, data) => sum + data.length, 0); + let writtenBytes = 0; + onProgress?.(0); for (const [startAddress, data] of bootloaderMemoryMap.entries()) { const dataOption: DataOption = { startAddress, data }; - await kboot.writeMemory(dataOption); + const bytesBeforeEntry = writtenBytes; + await kboot.writeMemory(dataOption, entryPercent => { + onProgress?.(Math.min(100, Math.round((bytesBeforeEntry + data.length * entryPercent / 100) / totalBytes * 100))); + }); + writtenBytes += data.length; } this.logService.misc('[UhkOperations] Reset bootloader'); @@ -178,7 +185,7 @@ export class UhkOperations { this.logService.misc('[UhkOperations] Right firmware successfully flashed'); } - public async updateFirmwareWithMcuManager(firmwarePath: string, device: UhkDeviceProduct) { + public async updateFirmwareWithMcuManager(firmwarePath: string, device: UhkDeviceProduct, onProgress?: (percent: number) => void) { if (!(await existsAsync(firmwarePath))) { throw new Error(`Firmware path not found: ${firmwarePath}`); } @@ -199,7 +206,7 @@ export class UhkOperations { this.logService.misc(`[UhkOperations] Read ${device.logName} firmware from file`); const configData = fs.readFileSync(firmwarePath); this.logService.misc('[UhkOperations] Write memory with mcumgr'); - await mcuManager.imageUpload(configData); + await mcuManager.imageUpload(configData, onProgress); this.logService.misc('[UhkOperations] Reset mcu bootloader'); await mcuManager.reset(); this.logService.misc('[UhkOperations] Close mcu communication channels'); @@ -207,14 +214,15 @@ export class UhkOperations { this.logService.misc(`[UhkOperations] ${device.logName} firmware successfully flashed`); } - public async updateLeftModuleWithKboot(firmwarePath: string, device: UhkDeviceProduct): Promise { - return this.updateModuleWithKboot(firmwarePath, device, LEFT_HALF_MODULE); + public async updateLeftModuleWithKboot(firmwarePath: string, device: UhkDeviceProduct, onProgress?: (percent: number) => void): Promise { + return this.updateModuleWithKboot(firmwarePath, device, LEFT_HALF_MODULE, onProgress); } public async updateModuleWithKboot( firmwarePath: string, device: UhkDeviceProduct, - module: UhkModule + module: UhkModule, + onProgress?: (percent: number) => void ): Promise { this.logService.misc(`[UhkOperations] Start flashing "${module.name}" module firmware`); await this.device.reenumerate({ @@ -279,7 +287,7 @@ export class UhkOperations { this.logService.misc('[UhkOperations] Write memory'); await kboot.configureI2c(module.i2cAddress); - await kboot.writeMemory({ startAddress: 0, data: configData }); + await kboot.writeMemory({ startAddress: 0, data: configData }, onProgress); this.logService.misc(`[UhkOperations] Reset "${module.name}" keyboard`); await kboot.reset(); diff --git a/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.html b/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.html index 8b66db18a84..1e6169f4981 100644 --- a/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.html +++ b/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.html @@ -7,36 +7,57 @@

Firmware

-
    -
  • +
      +
    • - - - - - - {{ state.moduleName }} - firmware: - {{ state.gitRepo }}: - {{ state.currentFirmwareVersion }} - {{ gitTagText(state.currentFirmwareVersion, state.gitTag) }} - - {{ state.newFirmwareVersion }} {{ gitTagText(state.newFirmwareVersion, state.newGitTag) }} +
      + + + + - - is detected, and its firmware is implemented by the right keyboard half. + {{ state.moduleName }} + firmware: + {{ state.gitRepo }}: + {{ state.currentFirmwareVersion }} + {{ gitTagText(state.currentFirmwareVersion, state.gitTag) }} + + {{ state.newFirmwareVersion }} {{ gitTagText(state.newFirmwareVersion, state.newGitTag) }} + + + is detected, and its firmware is implemented by the right keyboard half. + {{ state.progressPercent || 0 }}% +
      +
      +
      +
    + +

    Firmware update doesn't work on Windows 7, Windows Vista, and Windows XP. Use Windows 10, Windows 8, Linux, or OSX instead.

    @@ -118,9 +139,13 @@

    role="alert"> {{ upgradeType }} update succeeded. + +

    + {{ showLog ? 'Hide' : 'Show' }} update log +

    -
    +
    -

    - {{ showLog ? 'Hide' : 'Show' }} update log +

    + {{ showLog ? 'Hide' : 'Show' }} update log

    diff --git a/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.scss b/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.scss index 37d1c0b74a3..bd02030af85 100644 --- a/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.scss +++ b/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.scss @@ -11,10 +11,6 @@ width: 1rem; } -.firmware-module-list { - margin-bottom: 0; -} - .firmware-module { width: fit-content; max-width: 100%; @@ -54,11 +50,3 @@ .firmware-connect-prompt__icon { font-size: 1.5rem; } - -.firmware-log-toggle { - margin-bottom: 0; - - a { - cursor: pointer; - } -} diff --git a/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.ts b/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.ts index 81e3911ca18..94de6be54f5 100644 --- a/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.ts +++ b/packages/uhk-web/src/app/components/device/firmware/device-firmware.component.ts @@ -26,7 +26,12 @@ import { import { RecoveryModuleAction, UpdateFirmwareAction, UpdateFirmwareWithAction } from '../../../store/actions/device'; import { XtermLog } from '../../../models/xterm-log'; import { XtermComponent } from '../../xterm/xterm.component'; -import { FirmwareUpgradeState, ModuleFirmwareUpgradeState, ModuleFirmwareUpgradeStates, UpdateFirmwareWithPayload } from '../../../models'; +import { + FirmwareUpgradeState, + ModuleFirmwareUpgradeState, + ModuleFirmwareUpgradeStates, + UpdateFirmwareWithPayload, +} from '../../../models'; @Component({ selector: 'device-firmware', From eb4495aa1f36893dc93ced1e6203aae232c5b260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiss=20R=C3=B3bert?= Date: Thu, 9 Jul 2026 14:23:58 +0200 Subject: [PATCH 5/6] fix: remove unnecessary bytesBeforeEntry --- packages/mcumgr/src/mcumgr.ts | 2 ++ packages/uhk-agent/src/services/device.service.ts | 1 + packages/uhk-usb/src/uhk-operations.ts | 13 ++++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/mcumgr/src/mcumgr.ts b/packages/mcumgr/src/mcumgr.ts index 632ed78185c..03297fe53a6 100644 --- a/packages/mcumgr/src/mcumgr.ts +++ b/packages/mcumgr/src/mcumgr.ts @@ -108,6 +108,8 @@ export class McuManager { logger('Image uploaded: %d', written / buffer.length * 100); onProgress?.(Math.min(100, Math.round(written / buffer.length * 100))); } + + onProgress?.(100); } /** diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts index d89757d26d1..9c562eb5fea 100644 --- a/packages/uhk-agent/src/services/device.service.ts +++ b/packages/uhk-agent/src/services/device.service.ts @@ -1553,6 +1553,7 @@ export class DeviceService { } await waitForUhkDeviceConnected(device); + this.firmwareUpgradePrompt(eventSender, undefined) } diff --git a/packages/uhk-usb/src/uhk-operations.ts b/packages/uhk-usb/src/uhk-operations.ts index 0503611b3ae..d83121b429e 100644 --- a/packages/uhk-usb/src/uhk-operations.ts +++ b/packages/uhk-usb/src/uhk-operations.ts @@ -47,14 +47,14 @@ import { UsbCommand, UsbVariables } from './constants.js'; -import { UpdateLeftModuleWithKbootOptions } from './models/index.js'; -import { UpdateModuleWithKbootOptions } from './models/index.js'; import { DebugInfo, Duration, I2cBaudRate, I2cErrorBuffer, LoadConfigurationsResult, + UpdateLeftModuleWithKbootOptions, + UpdateModuleWithKbootOptions, } from './models/index.js'; import { UhkHidDevice } from './uhk-hid-device.js'; @@ -166,17 +166,18 @@ export class UhkOperations { this.logService.misc('[UhkOperations] Read RIGHT firmware from file'); const bootloaderMemoryMap = await readBootloaderFirmwareFromHexFileAsync(firmwarePath); this.logService.misc('[UhkOperations] Write memory'); + const totalBytes = [...bootloaderMemoryMap.values()].reduce((sum, data) => sum + data.length, 0); let writtenBytes = 0; + onProgress?.(0); - for (const [startAddress, data] of bootloaderMemoryMap.entries()) { - const bytesBeforeEntry = writtenBytes; + for (const [startAddress, data] of bootloaderMemoryMap.entries()) { const dataOption: DataOption = { startAddress, data, onProgress: entryPercent => { - onProgress?.(Math.min(100, Math.round((bytesBeforeEntry + data.length * entryPercent / 100) / totalBytes * 100))); + onProgress?.(Math.min(100, Math.round((writtenBytes + data.length * entryPercent / 100) / totalBytes * 100))); }, }; @@ -184,6 +185,8 @@ export class UhkOperations { writtenBytes += data.length; } + onProgress?.(100); + this.logService.misc('[UhkOperations] Reset bootloader'); await kboot.reset(); this.logService.misc('[UhkOperations] Close communication channels'); From 6c4d69c3d0e9157477dcb905e23ff5a87c3e1348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiss=20R=C3=B3bert?= Date: Thu, 9 Jul 2026 14:33:32 +0200 Subject: [PATCH 6/6] fix: log entry prefix --- packages/uhk-agent/src/services/device.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts index 9c562eb5fea..63ca80b6a6d 100644 --- a/packages/uhk-agent/src/services/device.service.ts +++ b/packages/uhk-agent/src/services/device.service.ts @@ -1549,7 +1549,7 @@ export class DeviceService { message: string ): Promise { if (!(await isUhkDeviceConnected(device))) { - this.firmwareUpgradePrompt(eventSender, `[DeviceService] show firmware upgrade prompt: ${message}`) + this.firmwareUpgradePrompt(eventSender, message) } await waitForUhkDeviceConnected(device);