Skip to content

Commit

Permalink
Merge pull request #2500 from UltimateHackingKeyboard/feat-delete-dev…
Browse files Browse the repository at this point in the history
…ice-pairings

feat: delete device pairings
  • Loading branch information
mondalaci authored Feb 26, 2025
2 parents d9748f0 + 0c32251 commit 17b7d7e
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 29 deletions.
74 changes: 74 additions & 0 deletions packages/uhk-agent/src/services/device.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { emptyDir } from 'fs-extra';
import { cloneDeep, isEqual } from 'lodash';
import os from 'os';
import {
AreBleAddressesPairedIpcResponse,
ALL_UHK_DEVICES,
BackupUserConfigurationInfo,
ChangeKeyboardLayoutIpcResponse,
Expand Down Expand Up @@ -129,6 +130,15 @@ export class DeviceService {
this.logService.misc('[DeviceService] Cannot query udev info:', error);
});

ipcMain.on(IpcEvents.device.areBleAddressesPaired, (...args: any[]) => {
this.queueManager.add({
method: this.areBleAddressesPaired,
bind: this,
params: args,
asynchronous: true
});
});

ipcMain.on(IpcEvents.device.changeKeyboardLayout, (...args: any[]) => {
this.queueManager.add({
method: this.changeKeyboardLayout,
Expand All @@ -147,6 +157,15 @@ export class DeviceService {
});
});

ipcMain.on(IpcEvents.device.eraseBleSettings, (...args: any[]) => {
this.queueManager.add({
method: this.eraseBleSettings,
bind: this,
params: args,
asynchronous: true
});
});

ipcMain.on(IpcEvents.device.toggleI2cDebugging, this.toggleI2cDebugging.bind(this));

ipcMain.on(IpcEvents.device.saveUserConfiguration, (...args: any[]) => {
Expand Down Expand Up @@ -239,6 +258,37 @@ export class DeviceService {
logService.misc('[DeviceService] init success');
}

public async areBleAddressesPaired(event: Electron.IpcMainEvent, args: Array<any>): Promise<void> {
this.logService.misc('[DeviceService] Check BLE Addresses are paired');

const response: AreBleAddressesPairedIpcResponse = {
success: true,
addresses: {},
};

try {
await this.stopPollUhkDevice();

const addresses: string[] = args[0];
for (const address of addresses) {
response.addresses[address] = await this.device.isPairedWith(convertBleStringToNumberArray(address));
}
}
catch (error) {
this.logService.error('[DeviceService] Check BLE Addresses pairing failed');
response.success = false;
response.error = {
message: error.message,
};
}
finally {
this.savedState = undefined;
this.startPollUhkDevice();
}

event.sender.send(IpcEvents.device.areBleAddressesPairedReply, response);
}

/**
* Return with the actual UserConfiguration from UHK Device
* @returns {Promise<Buffer>}
Expand Down Expand Up @@ -830,6 +880,30 @@ export class DeviceService {
}
}

public async eraseBleSettings(event: Electron.IpcMainEvent): Promise<void> {
this.logService.misc('[DeviceService] erase BLE Settings');
const response: IpcResponse = {
success: true,
};
try {
await this.stopPollUhkDevice();
await this.operations.eraseBleSettings();
this.logService.misc('[DeviceService] erase BLE settings success');
}
catch(error) {
this.logService.error('[DeviceService] erase BLE settings failed', error);
response.success = false;
response.error = {
message: error.message,
};
}
finally {
this.startPollUhkDevice();
}

event.sender.send(IpcEvents.device.eraseBleSettingsReply, response);
}

public async startDonglePairing(event: Electron.IpcMainEvent): Promise<void> {
this.logService.misc('[DeviceService] start Dongle pairing');
try {
Expand Down
4 changes: 4 additions & 0 deletions packages/uhk-common/src/models/ipc-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ export class FirmwareUpgradeIpcResponse extends IpcResponse {
userConfigSaved?: boolean;
firmwareDowngraded?: boolean;
}

export interface AreBleAddressesPairedIpcResponse extends IpcResponse {
addresses: Record<string, boolean>;
}
4 changes: 4 additions & 0 deletions packages/uhk-common/src/util/ipcEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ export class AutoUpdate {
}

export class Device {
public static readonly areBleAddressesPaired = 'device-are-ble-addresses-paired';
public static readonly areBleAddressesPairedReply = 'device-are-ble-addresses-paired-reply';
public static readonly changeKeyboardLayout = 'device-change-keyboard-layout';
public static readonly changeKeyboardLayoutReply = 'device-change-keyboard-layout-reply';
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 hardwareModulesLoaded = 'device-hardware-modules-loaded';
public static readonly setPrivilegeOnLinux = 'set-privilege-on-linux';
public static readonly setPrivilegeOnLinuxReply = 'set-privilege-on-linux-reply';
Expand Down
1 change: 1 addition & 0 deletions packages/uhk-usb/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export enum UsbCommand {
UnpairAll = 0x1a,
IsPaired = 0x1b,
EnterPairingMode = 0x1c,
EraseBleSettings = 0x1d,
}

export enum EepromOperation {
Expand Down
6 changes: 6 additions & 0 deletions packages/uhk-usb/src/uhk-operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ export class UhkOperations {
private device: UhkHidDevice) {
}

public async eraseBleSettings(): Promise<void> {
this.logService.usbOps('[UhkHidDevice] USB[T]: Erase BLE settings.');
const transfer = Buffer.from([UsbCommand.EraseBleSettings]);
await this.device.write(transfer);
}

public async jumpToBootloaderModule(module: ModuleSlotToId): Promise<void> {
this.logService.usbOps(`[UhkHidDevice] USB[T]: Jump to bootloader. Module: ${ModuleSlotToId[module].toString()}`);
const transfer = Buffer.from([UsbCommand.JumpToModuleBootloader, module]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,44 @@ <h1>
>
</td>
<td>
<button *ngIf="hostConnection.hasAddress()"
[disabled]="isDonglePairing"
(confirm)="deleteHostConnection(index, hostConnection)"
type="button"
class="btn"
mwlConfirmationPopover
popoverTitle="Do you really want to delete this connection?"
placement="bottom"
confirmText="Yes"
cancelText="No"
>
<fa-icon [icon]="faTrash" />
</button>
<div class="d-inline-flex align-items-center gap-2">
<button *ngIf="hostConnection.hasAddress()"
[disabled]="eraseBleSettingsButtonState.disabled"
(confirm)="deleteHostConnection(index, hostConnection)"
type="button"
class="btn"
mwlConfirmationPopover
popoverTitle="Do you really want to delete this connection?"
placement="bottom"
confirmText="Yes"
cancelText="No"
>
<fa-icon [icon]="faTrash" />
</button>

<fa-icon *ngIf="showNotPairedTooltip(hostConnection)"
[icon]="faCircleExclamation"
ngbTooltip="This connection has been unpaired. You can re-pair this device."
/>
</div>
</td>
</tr>
</ng-container>
</tbody>
</table>

<button *ngIf="eraseBleSettingsButtonState.visible"
class="btn btn-danger mt-3"
mwlConfirmationPopover
popoverTitle="Are you sure?"
placement="bottom"
confirmText="Yes"
cancelText="No"
[disabled]="eraseBleSettingsButtonState.disabled"
(confirm)="eraseBleSettings()"
>
<fa-icon *ngIf="eraseBleSettingsButtonState.erasing"
[icon]="faSpinner"
animation="spin" />
Delete device pairings
</button>
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { ChangeDetectorRef } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { DragulaService } from '@ert78gb/ng2-dragula';
import { faCircleNodes, faTrash } from '@fortawesome/free-solid-svg-icons';
import { faCircleExclamation, faCircleNodes, faSpinner, faTrash } from '@fortawesome/free-solid-svg-icons';
import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs';
import { HostConnection } from 'uhk-common';

import { EraseBleSettingsButtonState } from '../../../models';
import { CheckAreHostConnectionsPairedAction, EraseBleSettingAction } from '../../../store/actions/device';
import { DeleteHostConnectionAction } from '../../../store/actions/dongle-pairing.action';
import {
RenameHostConnectionAction,
ReorderHostConnectionsAction,
SetHostConnectionSwitchoverAction,
} from '../../../store/actions/user-config';
import { AppState, getHostConnections, isDonglePairing } from '../../../store/index';
import { AppState, getEraseBleSettingsButtonState, getHostConnections, getHostConnectionPairState } from '../../../store/index';

@Component({
selector: 'host-connections',
Expand All @@ -24,19 +26,25 @@ import { AppState, getHostConnections, isDonglePairing } from '../../../store/in
})
export class HostConnectionsComponent implements OnInit, OnDestroy {
faCircleNodes = faCircleNodes;
faSpinner = faSpinner;
faTrash = faTrash;
faCircleExclamation = faCircleExclamation;

hostConnectionPairState: Record<string, boolean> = {};
eraseBleSettingsButtonState: EraseBleSettingsButtonState;
hostConnections: HostConnection[] = [] as HostConnection[];
isDonglePairing: boolean;
dragAndDropGroup = 'HOST_CONNECTION';

private hostConnectionPairStateSubscription: Subscription;
private eraseBleSettingsSubscription: Subscription;
private hostConnectionsSubscription: Subscription;
private isDonglePairingSubscription: Subscription;

constructor(private dragulaService: DragulaService,
private cdRef: ChangeDetectorRef,
private store: Store<AppState>) {

this.store.dispatch(new CheckAreHostConnectionsPairedAction());

dragulaService.createGroup(this.dragAndDropGroup, {
moves: (el, container, handle) => {
if (!handle) {
Expand All @@ -57,30 +65,38 @@ export class HostConnectionsComponent implements OnInit, OnDestroy {
}

ngOnInit(): void {
this.eraseBleSettingsSubscription = this.store.select(getEraseBleSettingsButtonState)
.subscribe(eraseBleSettingsButtonState => {
this.eraseBleSettingsButtonState = eraseBleSettingsButtonState;
this.cdRef.markForCheck();
});
this.hostConnectionPairStateSubscription = this.store.select(getHostConnectionPairState)
.subscribe(hostConnectionPairState => {
this.hostConnectionPairState = hostConnectionPairState;
this.cdRef.markForCheck();
});
this.hostConnectionsSubscription = this.store.select(getHostConnections)
.subscribe(hostConnections => {
this.hostConnections = hostConnections;
this.cdRef.markForCheck();
});
this.isDonglePairingSubscription = this.store.select(isDonglePairing)
.subscribe(isDonglePairing => {
this.isDonglePairing = isDonglePairing;
this.cdRef.markForCheck();
});
}

ngOnDestroy(): void {
this.dragulaService.destroy(this.dragAndDropGroup);
if(this.hostConnectionsSubscription) {
this.hostConnectionsSubscription.unsubscribe();
}
this.isDonglePairingSubscription?.unsubscribe();
this.eraseBleSettingsSubscription?.unsubscribe();
this.hostConnectionPairStateSubscription?.unsubscribe();
this.hostConnectionsSubscription?.unsubscribe();
}

deleteHostConnection(index: number, hostConnection: HostConnection): void {
this.store.dispatch(new DeleteHostConnectionAction({index, hostConnection}));
}

eraseBleSettings(): void {
this.store.dispatch(new EraseBleSettingAction());
}

renameHostConnection(index: number, newName: string): void {
this.store.dispatch(new RenameHostConnectionAction({
index,
Expand All @@ -95,4 +111,8 @@ export class HostConnectionsComponent implements OnInit, OnDestroy {
setHostConnectionSwitchover(index: number, checked: boolean): void {
this.store.dispatch(new SetHostConnectionSwitchoverAction({index, checked}));
}

showNotPairedTooltip(hostConnection: HostConnection): boolean {
return hostConnection.hasAddress() && !this.hostConnectionPairState[hostConnection.address];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface EraseBleSettingsButtonState {
disabled: boolean;
erasing: boolean;
visible: boolean;
}
1 change: 1 addition & 0 deletions packages/uhk-web/src/app/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './delete-host-connection-payload';
export * from './device-ui-states';
export * from './dongle-pairing-state';
export * from './duplicate-macro-action-payload';
export * from './erase-ble-settings-button-state';
export * from './exchange-keys-action.model';
export * from './firmware-upgrade-state';
export * from './firmware-upgrade-steps';
Expand Down
19 changes: 19 additions & 0 deletions packages/uhk-web/src/app/services/device-renderer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable, NgZone } from '@angular/core';
import { Action, Store } from '@ngrx/store';

import {
AreBleAddressesPairedIpcResponse,
ChangeKeyboardLayoutIpcResponse,
CurrentlyUpdatingModuleInfo,
DeviceConnectionState,
Expand Down Expand Up @@ -35,8 +36,10 @@ import {
import { IpcCommonRenderer } from './ipc-common-renderer';
import {
ChangeKeyboardLayoutReplyAction,
CheckAreHostConnectionsPairedReplyAction,
ConnectionStateChangedAction,
DongleVersionInfoLoadedAction,
EraseBleSettingReplyAction,
CurrentlyUpdateSkipModuleAction,
CurrentlyUpdatingModuleAction,
HardwareModulesLoadedAction,
Expand Down Expand Up @@ -67,6 +70,10 @@ export class DeviceRendererService {
this.logService.misc('[DeviceRendererService] init success ');
}

areBleAddressesPaired(addresses: string[]): void {
this.ipcRenderer.send(IpcEvents.device.areBleAddressesPaired, addresses);
}

changeKeyboardLayout(layout: KeyboardLayout, hardwareConfiguration: HardwareConfiguration): void {
this.ipcRenderer.send(IpcEvents.device.changeKeyboardLayout, layout, hardwareConfiguration.toJsonObject());
}
Expand All @@ -79,6 +86,10 @@ export class DeviceRendererService {
});
}

eraseBleSettings(): void {
this.ipcRenderer.send(IpcEvents.device.eraseBleSettings);
}

setPrivilegeOnLinux(): void {
this.ipcRenderer.send(IpcEvents.device.setPrivilegeOnLinux);
}
Expand Down Expand Up @@ -139,6 +150,10 @@ export class DeviceRendererService {
}

private registerEvents(): void {
this.ipcRenderer.on(IpcEvents.device.areBleAddressesPairedReply, (event: string, response: AreBleAddressesPairedIpcResponse) => {
this.dispachStoreAction(new CheckAreHostConnectionsPairedReplyAction(response));
});

this.ipcRenderer.on(IpcEvents.device.changeKeyboardLayoutReply, (event: string, response: ChangeKeyboardLayoutIpcResponse) => {
this.dispachStoreAction(new ChangeKeyboardLayoutReplyAction(response));
});
Expand All @@ -155,6 +170,10 @@ export class DeviceRendererService {
this.dispachStoreAction(new DeleteHostConnectionFailedAction(message));
});

this.ipcRenderer.on(IpcEvents.device.eraseBleSettingsReply, (event: string, response: IpcResponse) => {
this.dispachStoreAction(new EraseBleSettingReplyAction(response));
});

this.ipcRenderer.on(IpcEvents.device.hardwareModulesLoaded, (event: string, response: HardwareModules) => {
this.dispachStoreAction(new HardwareModulesLoadedAction(response));
});
Expand Down
Loading

0 comments on commit 17b7d7e

Please sign in to comment.