diff --git a/packages/kboot/src/models/data-option.ts b/packages/kboot/src/models/data-option.ts index 246a108c3b2..9119cc39a82 100644 --- a/packages/kboot/src/models/data-option.ts +++ b/packages/kboot/src/models/data-option.ts @@ -1,5 +1,8 @@ +type ProgressCallback = (percent: number) => void; + export interface DataOption { startAddress: number; data: Buffer | Uint8Array; + onProgress?: ProgressCallback; timeout?: number; } diff --git a/packages/kboot/src/usb-peripheral.ts b/packages/kboot/src/usb-peripheral.ts index a2f33f2f0cd..9de97290689 100644 --- a/packages/kboot/src/usb-peripheral.ts +++ b/packages/kboot/src/usb-peripheral.ts @@ -75,6 +75,8 @@ export class UsbPeripheral implements Peripheral { async writeMemory(option: DataOption): Promise { try { + option.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); + option.onProgress?.(Math.min(100, Math.round((i + slice.length) / option.data.length * 100))); } const receivedData = await this._device.read(option.timeout || 2000); @@ -127,6 +130,8 @@ export class UsbPeripheral implements Peripheral { const msg = `Non zero write memory final response! Response code: ${secondCommandResponse.code}`; throw new Error(msg); } + + option.onProgress?.(100); } catch (err) { logger('Can not write memory data %O', err); throw err; diff --git a/packages/kboot/test/test-peripheral.ts b/packages/kboot/test/test-peripheral.ts index 410995afc10..712f8d4328e 100644 --- a/packages/kboot/test/test-peripheral.ts +++ b/packages/kboot/test/test-peripheral.ts @@ -1,7 +1,7 @@ import { CommandOption, CommandResponse, DataOption, Peripheral, ResponseCodes, ResponseTags } from '../src/index.js'; export class TestPeripheral implements Peripheral { - close(): void { + close(): Promise { } async open(): Promise { diff --git a/packages/mcumgr/src/mcumgr.ts b/packages/mcumgr/src/mcumgr.ts index 867cc0ee091..03297fe53a6 100644 --- a/packages/mcumgr/src/mcumgr.ts +++ b/packages/mcumgr/src/mcumgr.ts @@ -13,6 +13,7 @@ import { } from './constants.js'; import { ImageUploadRequest } from './models/image-upload-request.js'; import { ImageUploadResponseData } from './models/image-upload-response-data.js'; +import { ProgressCallback } from './models/progress-callback.js'; import { NmpResponse } from './models/nmp.js'; import { Peripheral } from './peripheral.js'; import * as cbor from './util/cbor.js'; @@ -74,9 +75,10 @@ export class McuManager { /** * Upload a firmware/bootloader image to the device */ - async imageUpload(buffer: Buffer): Promise { + async imageUpload(buffer: Buffer, onProgress?: ProgressCallback): Promise { logger('Start send image upload command: %o', { bufferLength: buffer.byteLength }); let written = 0; + onProgress?.(0); while (written < buffer.length) { const message: ImageUploadRequest = { @@ -104,7 +106,10 @@ 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/mcumgr/src/models/index.ts b/packages/mcumgr/src/models/index.ts index 773c7fdedbb..060dae380ed 100644 --- a/packages/mcumgr/src/models/index.ts +++ b/packages/mcumgr/src/models/index.ts @@ -1,2 +1,3 @@ export * from './image-upload-request.js'; export * from './nmp.js'; +export * from './progress-callback.js'; diff --git a/packages/mcumgr/src/models/progress-callback.ts b/packages/mcumgr/src/models/progress-callback.ts new file mode 100644 index 00000000000..9cf063a2bed --- /dev/null +++ b/packages/mcumgr/src/models/progress-callback.ts @@ -0,0 +1 @@ +export type ProgressCallback = (percent: number) => void; diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts index a0930c95649..63ca80b6a6d 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, @@ -560,7 +562,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, this.createFirmwareProgressReporter(event.sender, UHK_DONGLE.name)); this.logService.misc('[DeviceService] Waiting for keyboard'); await waitForDevices(UHK_DONGLE.keyboard); await dongleUhkDevice.close(); @@ -609,7 +611,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, this.createFirmwareProgressReporter(event.sender, RIGHT_HALF_FIRMWARE_UPGRADE_MODULE_NAME)); this.logService.misc('[DeviceService] Waiting for keyboard'); await waitForDevices(uhkDeviceProduct.keyboard); @@ -673,28 +675,31 @@ 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.sender, + 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, this.createFirmwareProgressReporter(event.sender, leftModuleInfo.module.name)); - await waitForUhkDeviceConnected(uhkDeviceProduct); + await this.waitForUhkDeviceWithConnectPrompt( + event.sender, + 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 - ); + .updateModuleWithKboot({ + firmwarePath: getModuleFirmwarePath(leftModuleInfo.module, packageJson), + device: uhkDeviceProduct, + module: leftModuleInfo.module, + onProgress: this.createFirmwareProgressReporter(event.sender, leftModuleInfo.module.name), + prompt: (message) => this.firmwareUpgradePrompt(event.sender, message), + }); } } else { const moduleConfig = packageJson.modules.find(firmwareDevice => firmwareDevice.moduleId === leftModuleInfo.module.id); @@ -752,11 +757,13 @@ export class DeviceService { newFirmwareChecksum: moduleConfig.md5, } as CurrentlyUpdatingModuleInfo); await this.operations - .updateModuleWithKboot( - getModuleFirmwarePath(moduleInfo.module, packageJson), - uhkDeviceProduct, - moduleInfo.module - ); + .updateModuleWithKboot({ + firmwarePath: getModuleFirmwarePath(moduleInfo.module, packageJson), + device: uhkDeviceProduct, + module: moduleInfo.module, + onProgress: this.createFirmwareProgressReporter(event.sender, moduleInfo.module.name), + prompt: (message) => this.firmwareUpgradePrompt(event.sender, message), + }); this.logService.misc(`[DeviceService] "${moduleInfo.module.name}" firmware update done.`); } else { const moduleConfig = packageJson.modules.find(firmwareDevice => firmwareDevice.moduleId === moduleInfo.module.id); @@ -815,6 +822,7 @@ export class DeviceService { await this.dongleZephyrLogService.enable(); await this.leftHalfZephyrLogService.enable(); + event.sender.send(IpcEvents.device.firmwareUpgradeConnectPrompt, undefined); event.sender.send(IpcEvents.device.updateFirmwareReply, response); } @@ -897,11 +905,11 @@ export class DeviceService { this.logService.misc('[DeviceService] UHK Module: ', JSON.stringify(uhkModule)); await this.operations - .updateModuleWithKboot( - getModuleFirmwarePath(uhkModule, packageJson), - uhkDeviceProduct, - uhkModule - ); + .updateModuleWithKboot({ + firmwarePath: getModuleFirmwarePath(uhkModule, packageJson), + device: uhkDeviceProduct, + module: uhkModule + }); response.success = true; } catch (error) { @@ -1182,6 +1190,24 @@ export class DeviceService { this.win.webContents.send(IpcEvents.device.statusBufferChanged, message); } + private createFirmwareProgressReporter(eventSender: Electron.WebContents, moduleName: string) { + let lastProgress = 0; + + return (percent: number) => { + const progress = Math.max(0, Math.min(100, Math.round(percent))); + + if (progress === lastProgress) { + return; + } + + lastProgress = progress; + eventSender.send(IpcEvents.device.moduleFirmwareUpgradeProgress, { + moduleName, + progress, + } as ModuleFirmwareUpgradeProgress); + }; + } + /** * HID API not support device attached and detached event. * This method check the keyboard is attached to the computer or not. @@ -1516,4 +1542,26 @@ export class DeviceService { this.win.webContents.send(IpcEvents.device.zephyrLog, logEntry) } } + + private async waitForUhkDeviceWithConnectPrompt( + eventSender: Electron.WebContents, + device: UhkDeviceProduct, + message: string + ): Promise { + if (!(await isUhkDeviceConnected(device))) { + this.firmwareUpgradePrompt(eventSender, message) + } + + await waitForUhkDeviceConnected(device); + + this.firmwareUpgradePrompt(eventSender, undefined) + } + + private firmwareUpgradePrompt( + eventSender: Electron.WebContents, + message: string + ): void { + this.logService.misc(`[DeviceService] firmware upgrade prompt: ${message}`); + eventSender.send(IpcEvents.device.firmwareUpgradeConnectPrompt, { message } as FirmwareUpgradeConnectPrompt); + } } 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..d5863a2a207 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,12 +24,15 @@ 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'; export * from './module-version-info.js'; export * from './log-options.js'; export * from './hardware-modules.js'; +export * from './progress-callback.js'; +export * from './prompt-callback.js'; export * from './rgb-color-interface.js'; export * from './right-module-info.js'; export * from './right-slot-modules.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/models/progress-callback.ts b/packages/uhk-common/src/models/progress-callback.ts new file mode 100644 index 00000000000..9cf063a2bed --- /dev/null +++ b/packages/uhk-common/src/models/progress-callback.ts @@ -0,0 +1 @@ +export type ProgressCallback = (percent: number) => void; diff --git a/packages/uhk-common/src/models/prompt-callback.ts b/packages/uhk-common/src/models/prompt-callback.ts new file mode 100644 index 00000000000..1567efc1de3 --- /dev/null +++ b/packages/uhk-common/src/models/prompt-callback.ts @@ -0,0 +1 @@ +export type PromptCallback = (message: string) => void; 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/models/index.ts b/packages/uhk-usb/src/models/index.ts index 259e42bc837..75cbf676657 100644 --- a/packages/uhk-usb/src/models/index.ts +++ b/packages/uhk-usb/src/models/index.ts @@ -8,3 +8,4 @@ export * from './pairing-info.js'; export * from './reenumerate-option.js'; export * from './reenumerate-result.js'; export * from './tmp-firmware.js'; +export * from './update-module-with-kboot-options.js'; diff --git a/packages/uhk-usb/src/models/update-module-with-kboot-options.ts b/packages/uhk-usb/src/models/update-module-with-kboot-options.ts new file mode 100644 index 00000000000..c05d838f220 --- /dev/null +++ b/packages/uhk-usb/src/models/update-module-with-kboot-options.ts @@ -0,0 +1,18 @@ +import { + ProgressCallback, + PromptCallback, + UhkModule, + UhkDeviceProduct, +} from 'uhk-common'; + +export interface UpdateLeftModuleWithKbootOptions { + device: UhkDeviceProduct, + firmwarePath: string, + onProgress?: ProgressCallback + prompt?: PromptCallback +} + + +export interface UpdateModuleWithKbootOptions extends UpdateLeftModuleWithKbootOptions{ + module: UhkModule, +} diff --git a/packages/uhk-usb/src/uhk-operations.ts b/packages/uhk-usb/src/uhk-operations.ts index 3ae27fd0a80..d83121b429e 100644 --- a/packages/uhk-usb/src/uhk-operations.ts +++ b/packages/uhk-usb/src/uhk-operations.ts @@ -19,6 +19,8 @@ import { LogService, ModuleSlotToId, ModuleVersionInfo, + ProgressCallback, + PromptCallback, UhkBuffer, UhkDeviceProduct, UHK_EEPROM_SIZE, @@ -51,6 +53,8 @@ import { I2cBaudRate, I2cErrorBuffer, LoadConfigurationsResult, + UpdateLeftModuleWithKbootOptions, + UpdateModuleWithKbootOptions, } from './models/index.js'; import { UhkHidDevice } from './uhk-hid-device.js'; @@ -123,22 +127,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?: ProgressCallback): 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?: ProgressCallback): Promise { if (!(await existsAsync(firmwarePath))) { throw new Error(`Firmware path not found: ${firmwarePath}`); } @@ -162,15 +166,27 @@ 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 + data, + onProgress: entryPercent => { + onProgress?.(Math.min(100, Math.round((writtenBytes + data.length * entryPercent / 100) / totalBytes * 100))); + }, }; await kboot.writeMemory(dataOption); + writtenBytes += data.length; } + onProgress?.(100); + this.logService.misc('[UhkOperations] Reset bootloader'); await kboot.reset(); this.logService.misc('[UhkOperations] Close communication channels'); @@ -178,7 +194,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?: ProgressCallback) { if (!(await existsAsync(firmwarePath))) { throw new Error(`Firmware path not found: ${firmwarePath}`); } @@ -199,7 +215,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,15 +223,20 @@ 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(options: UpdateLeftModuleWithKbootOptions): Promise { + return this.updateModuleWithKboot({ + ...options, + module: LEFT_HALF_MODULE + }); } - public async updateModuleWithKboot( - firmwarePath: string, - device: UhkDeviceProduct, - module: UhkModule - ): Promise { + public async updateModuleWithKboot({ + firmwarePath, + device, + module, + onProgress, + prompt, + }: UpdateModuleWithKbootOptions): Promise { this.logService.misc(`[UhkOperations] Start flashing "${module.name}" module firmware`); await this.device.reenumerate({ device, @@ -228,7 +249,7 @@ export class UhkOperations { await this.jumpToBootloaderModule(module.slotId); await this.device.close(); - const moduleBricked = await this.waitForKbootIdle(module.name); + const moduleBricked = await this.waitForKbootIdle(module.name, prompt); if (!moduleBricked) { const msg = `[UhkOperations] Couldn't connect to the "${module.name}".`; this.logService.error(msg); @@ -279,7 +300,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(); @@ -308,7 +329,7 @@ export class UhkOperations { * Return with the actual UserConfiguration from UHK Device * @returns {Promise} */ - public async loadConfigurations(onProgress?: (percent: number) => void): Promise { + public async loadConfigurations(onProgress?: ProgressCallback): Promise { try { await this.waitUntilKeyboardBusy(); onProgress?.(0); @@ -422,7 +443,7 @@ export class UhkOperations { }; } - public async saveUserConfiguration(buffer: Buffer, onProgress?: (percent: number) => void): Promise { + public async saveUserConfiguration(buffer: Buffer, onProgress?: ProgressCallback): Promise { const reportProgress = (percent: number) => { onProgress?.(percent); }; @@ -529,16 +550,24 @@ export class UhkOperations { } } - public async waitForKbootIdle(moduleName: string): Promise { + public async waitForKbootIdle(moduleName: string, prompt?: PromptCallback): Promise { + let promptSent = false; while (true) { const buffer = await this.device.write(Buffer.from([UsbCommand.GetProperty, DevicePropertyIds.CurrentKbootCommand])); await this.device.close(); if (buffer[1] === 0) { + prompt?.(undefined); return true; } - this.logService.misc(`[DeviceOperation] Cannot ping the bootloader. Please remove the "${moduleName}" module, and keep reconnecting it until you do not see this message anymore.`); + const message = `[DeviceOperation] Cannot ping the bootloader. Please remove the "${moduleName}" module, and keep reconnecting it until you do not see this message anymore.` + if (!promptSent) { + prompt?.(message); + promptSent = true; + } + + this.logService.misc(message); await snooze(1000); } @@ -965,7 +994,7 @@ export class UhkOperations { private async sendConfigToKeyboard( buffer: Buffer, isUserConfiguration, - onProgress?: (percent: number) => void + onProgress?: ProgressCallback ): Promise { const command = isUserConfiguration ? UsbCommand.WriteStagingUserConfig 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..deaab6fbda0 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 +

    -
    +