From 70bd27e343c35b03d2c641a590bc30cd16b274d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiss=20R=C3=B3bert?= Date: Sun, 22 Mar 2026 19:06:05 +0100 Subject: [PATCH] feat: command prompt for zephyr log --- .../uhk-agent/src/services/device.service.ts | 27 ++++++++++++++ .../src/services/zephyr-log.service.ts | 31 ++++++++++++++++ packages/uhk-common/src/util/ipcEvents.ts | 3 ++ packages/uhk-usb/src/uhk-operations.ts | 14 ++++++++ .../advanced-settings.page.component.html | 36 +++++++++++++++++-- .../advanced-settings.page.component.ts | 33 ++++++++++++++--- .../app/components/xterm/xterm.component.html | 2 +- .../app/components/xterm/xterm.component.scss | 8 +++-- .../app/services/device-renderer.service.ts | 12 +++++++ .../uhk-web/src/app/store/actions/device.ts | 24 +++++++++++++ .../uhk-web/src/app/store/effects/device.ts | 33 +++++++++++++++++ 11 files changed, 213 insertions(+), 10 deletions(-) diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts index bd3b1de4c04..08c95bd0f70 100644 --- a/packages/uhk-agent/src/services/device.service.ts +++ b/packages/uhk-agent/src/services/device.service.ts @@ -125,6 +125,7 @@ export class DeviceService { currentDeviceFn: getCurrentUhkDongleHID, logService: this.logService, ipcEvents: { + execShellCommand: IpcEvents.device.execShellCommandOnDongle, isZephyrLoggingEnabled: IpcEvents.device.isDongleZephyrLoggingEnabled, isZephyrLoggingEnabledReply: IpcEvents.device.isDongleZephyrLoggingEnabledReply, toggleZephyrLogging: IpcEvents.device.toggleDongleZephyrLogging, @@ -138,6 +139,7 @@ export class DeviceService { currentDeviceFn: getCurrenUhk80LeftHID, logService: this.logService, ipcEvents: { + execShellCommand: IpcEvents.device.execShellCommandOnLeftHalf, isZephyrLoggingEnabled: IpcEvents.device.isLeftHalfZephyrLoggingEnabled, isZephyrLoggingEnabledReply: IpcEvents.device.isLeftHalfZephyrLoggingEnabledReply, toggleZephyrLogging: IpcEvents.device.toggleLeftHalfZephyrLogging, @@ -195,6 +197,15 @@ export class DeviceService { }); }); + ipcMain.on(IpcEvents.device.execShellCommandOnRightHalf, (...args: any[]) => { + this.queueManager.add({ + method: this.execShellCommand, + bind: this, + params: args, + asynchronous: true + }); + }); + ipcMain.on(IpcEvents.device.toggleI2cDebugging, this.toggleI2cDebugging.bind(this)); ipcMain.on(IpcEvents.device.isRightHalfZephyrLoggingEnabled, (...args: any[]) => { @@ -973,6 +984,22 @@ export class DeviceService { event.sender.send(IpcEvents.device.eraseBleSettingsReply, response); } + public async execShellCommand(_: Electron.IpcMainEvent, [command]): Promise { + this.logService.misc(`[DeviceService] execute shell command: ${command}`); + + try { + await this.stopPollUhkDevice(); + await this.operations.execShellCommand(command); + this.logService.misc('[DeviceService] execute shell command success'); + } + catch(error) { + this.logService.error('[DeviceService] execute shell command failed', error); + } + finally { + this.startPollUhkDevice(); + } + } + public async startDonglePairing(event: Electron.IpcMainEvent): Promise { this.logService.misc('[DeviceService] start Dongle pairing'); try { diff --git a/packages/uhk-agent/src/services/zephyr-log.service.ts b/packages/uhk-agent/src/services/zephyr-log.service.ts index ab624d85083..e39991e4e53 100644 --- a/packages/uhk-agent/src/services/zephyr-log.service.ts +++ b/packages/uhk-agent/src/services/zephyr-log.service.ts @@ -11,6 +11,7 @@ export interface ZephyrLogServiceOptions { currentDeviceFn: typeof getCurrenUhk80LeftHID | typeof getCurrentUhkDongleHID; logService: LogService; ipcEvents: { + execShellCommand: string; isZephyrLoggingEnabled: string; isZephyrLoggingEnabledReply: string; toggleZephyrLogging: string; @@ -31,6 +32,15 @@ export class ZephyrLogService { private operationLimiter = pLimit(1); constructor(private options: ZephyrLogServiceOptions) { + ipcMain.on(options.ipcEvents.execShellCommand, (...args: any[]) => { + this.queueManager.add({ + method: this.execShellCommand, + bind: this, + params: args, + asynchronous: true + }); + }); + ipcMain.on(options.ipcEvents.isZephyrLoggingEnabled, (...args: any[]) => { this.queueManager.add({ method: this.isZephyrLoggingEnabled, @@ -81,6 +91,27 @@ export class ZephyrLogService { this.options.logService.misc(`[ZephyrLogService | ${this.options.uhkDeviceProduct.logName}] Disabled`); } + private async execShellCommand(_: Electron.IpcMainEvent, [command]): Promise { + try { + await this.pauseLogging(); + + const operations = await this.getOperations(); + if (!operations) { + const logEntry: ZephyrLogEntry = { + log: "Device is not connected. Can't execute shell command", + level: 'error', + device: this.options.uhkDeviceProduct.logName, + } + this.options.win.webContents.send(IpcEvents.device.zephyrLog, logEntry) + return; + } + await operations.execShellCommand(command); + } + finally { + await this.resumeLogging(); + } + } + private async getOperations(logEarlierInited = true): Promise { if (logEarlierInited) { this.options.logService.misc(`[ZephyrLogService | ${this.options.uhkDeviceProduct.logName}] getOperations`); diff --git a/packages/uhk-common/src/util/ipcEvents.ts b/packages/uhk-common/src/util/ipcEvents.ts index 386a2cd303e..61916764982 100644 --- a/packages/uhk-common/src/util/ipcEvents.ts +++ b/packages/uhk-common/src/util/ipcEvents.ts @@ -31,6 +31,9 @@ export class Device { public static readonly dongleVersionInfoLoaded = 'device-dongle-version-info-loaded'; public static readonly eraseBleSettings = 'device-erase-ble-settings'; public static readonly eraseBleSettingsReply = 'device-erase-ble-settings-reply'; + public static readonly execShellCommandOnDongle = 'device-exec-shell-command-on-dongle'; + public static readonly execShellCommandOnLeftHalf = 'device-exec-shell-command-on-left-half'; + public static readonly execShellCommandOnRightHalf = 'device-exec-shell-command-on-right-half'; public static readonly hardwareModulesLoaded = 'device-hardware-modules-loaded'; public static readonly isDongleZephyrLoggingEnabled = 'device-is-dongle-zephyr-logging-enabled'; public static readonly isDongleZephyrLoggingEnabledReply = 'device-is-dongle-zephyr-logging-enabled-reply'; diff --git a/packages/uhk-usb/src/uhk-operations.ts b/packages/uhk-usb/src/uhk-operations.ts index fe5575f2831..894cc41e283 100644 --- a/packages/uhk-usb/src/uhk-operations.ts +++ b/packages/uhk-usb/src/uhk-operations.ts @@ -937,4 +937,18 @@ export class UhkOperations { await this.device.write(buffer); } + + public async execShellCommand(cmd: string): Promise { + this.logService.usbOps('[DeviceOperation] USB[T]: Execute Shell Command'); + const b1 = Buffer.from([UsbCommand.ExecShellCommand]); + const b2 = Buffer.from(cmd); + const b0 = Buffer.from([0x00]); + const buffer = Buffer.concat([b1, b2, b0]); + + if (buffer.length > MAX_USB_PAYLOAD_SIZE) { + throw new Error('Shel command is too long. At most 61 characters are supported.') + } + + await this.device.write(buffer); + } } diff --git a/packages/uhk-web/src/app/components/device/advanced-settings/advanced-settings.page.component.html b/packages/uhk-web/src/app/components/device/advanced-settings/advanced-settings.page.component.html index 3e75682611b..740b2b024c6 100644 --- a/packages/uhk-web/src/app/components/device/advanced-settings/advanced-settings.page.component.html +++ b/packages/uhk-web/src/app/components/device/advanced-settings/advanced-settings.page.component.html @@ -79,7 +79,7 @@

-