Skip to content

Add mute/unmute HID actions #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions appliance-application/packages/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type Config = {
preferred_pin_screen: string;
hide_close_button: boolean;
debug: boolean;
keyboard_hid: boolean;
room: RoomConfig;
};
export type RoomConfig = {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion appliance-application/packages/main/src/HID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ export interface HID {
verificationAccepted(): void;
verificationRejected(): void;

connected(leave: () => void): void;
connected(actions: HIDActions): void;

disconnected(): void;

close(): Promise<void>;
}

export interface HIDActions {
leave: () => void;
mute: () => void;
unmute: () => void;
}
6 changes: 6 additions & 0 deletions appliance-application/packages/main/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import fs from 'fs';
import {DisplayManager} from './displayManager';
import type {HID} from './HID';
import type {Config} from '../../common/config.ts';
import {KeyboardHID} from '/@/keyboard';

export let displayManager: DisplayManager;
export const hdiDevices: HID[] = [];
Expand Down Expand Up @@ -121,4 +122,9 @@ async function loadHDIDevices() {
} catch (e) {
console.error(e);
}

if(config.keyboard_hid) {
hdiDevices.push(new KeyboardHID());
console.log('Keyboard HID registered');
}
}
77 changes: 77 additions & 0 deletions appliance-application/packages/main/src/keyboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type {HID, HIDActions} from './HID';
import { globalShortcut } from 'electron';

export class KeyboardHID implements HID {

private hasVerificationPending = false;

private acceptCallback: () => void;
private rejectCallback: () => void;

private leaveCallback: () => void;
private leaveCallback: () => void;
private muteCallback: () => void;
private unmuteCallback: () => void;
private isConnected: boolean;

constructor() {
globalShortcut.register('CommandOrControl+Alt+M', () => {
if (this.isConnected) {
this.muteCallback();
}
});
globalShortcut.register('CommandOrControl+Alt+U', () => {
if (this.isConnected) {
this.unmuteCallback();
}
});
globalShortcut.register('CommandOrControl+Alt+L', () => {
if (this.isConnected) {
this.leaveCallback();
}
});

globalShortcut.register('CommandOrControl+Alt+A', () => {
if (this.hasVerificationPending) {
this.acceptCallback();
}
});
globalShortcut.register('CommandOrControl+Alt+R', () => {
if (this.hasVerificationPending) {
this.rejectCallback();
}
});

}

requireVerification(accept: () => void, reject: () => void): void {
this.hasVerificationPending = true;

this.acceptCallback = accept;
this.rejectCallback = reject;
}

verificationAccepted(): void {
this.hasVerificationPending = false;
}

verificationRejected(): void {
this.hasVerificationPending = false;
}

async close(): Promise<void> {

}

connected(actions: HIDActions): void {

this.isConnected = true;
this.muteCallback = actions.mute;
this.unmuteCallback = actions.unmute;
this.leaveCallback = actions.leave;
}

disconnected(): void {
this.isConnected = false;
}
}
11 changes: 10 additions & 1 deletion appliance-application/packages/main/src/mainWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,18 @@ async function createWindow() {
ipcMain.off('pluginDisconnected', pluginDisconnected);
};


// Notify all connected HDI devices that the user has joined the meeting
hdiDevices.forEach(device => {
device.connected(leaveMeeting);
device.connected({
leave: leaveMeeting,
mute: () => {
bbbMeeting.mute();
},
unmute: () => {
bbbMeeting.unmute();
},
});
});
});

Expand Down
90 changes: 46 additions & 44 deletions appliance-application/packages/main/src/streamdeck.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as path from 'path';
import sharp from 'sharp';
import sharp, {Sharp} from 'sharp';
import type {StreamDeck} from '@elgato-stream-deck/node';
import {fileURLToPath} from 'url';
import type {HID} from './HID';
import type {HID, HIDActions} from './HID';
import type {StreamDeckButtonControlDefinitionLcdFeedback} from '@elgato-stream-deck/core/dist/controlDefinition';

const __filename = fileURLToPath(import.meta.url);
Expand All @@ -13,6 +13,8 @@ export class StreamDeckHID implements HID {
static ACCEPT_BUTTON: StreamDeckButtonControlDefinitionLcdFeedback;
static REJECT_BUTTON: StreamDeckButtonControlDefinitionLcdFeedback;
static LEAVE_BUTTON: StreamDeckButtonControlDefinitionLcdFeedback;
static MUTE_BUTTON: StreamDeckButtonControlDefinitionLcdFeedback;
static UNMUTE_BUTTON: StreamDeckButtonControlDefinitionLcdFeedback;

private streamDeck: StreamDeck;

Expand All @@ -21,13 +23,17 @@ export class StreamDeckHID implements HID {
private ACCEPT_IMG;
private REJECT_IMG;
private LEAVE_IMG;
private MUTE_IMG;
private UNMUTE_IMG;

private hasVerificationPending = false;

private acceptCallback: () => void;
private rejectCallback: () => void;

private leaveCallback: () => void;
private muteCallback: () => void;
private unmuteCallback: () => void;
private isConnected: boolean;

constructor(streamDeck: StreamDeck) {
Expand All @@ -44,22 +50,30 @@ export class StreamDeckHID implements HID {
this.streamDeck.on('up', button => {
console.log('key %d up', button.index);

if (button.index === StreamDeckHID.ACCEPT_BUTTON.index && this.hasVerificationPending) {
this.hasVerificationPending = false;
this.hideVerificationButtons();
this.acceptCallback();
}
if(this.hasVerificationPending){
if (button.index === StreamDeckHID.ACCEPT_BUTTON.index) {
this.acceptCallback();
}

if (button.index === StreamDeckHID.REJECT_BUTTON.index && this.hasVerificationPending) {
this.hasVerificationPending = false;
this.hideVerificationButtons();
this.rejectCallback();
if (button.index === StreamDeckHID.REJECT_BUTTON.index) {
this.rejectCallback();
}
}

if (button.index === StreamDeckHID.LEAVE_BUTTON.index) {
this.isConnected = true;
this.leaveCallback();
if(this.isConnected){
if (button.index === StreamDeckHID.MUTE_BUTTON.index) {
this.muteCallback();
}

if (button.index === StreamDeckHID.UNMUTE_BUTTON.index) {
this.unmuteCallback();
}

if (button.index === StreamDeckHID.LEAVE_BUTTON.index) {
this.leaveCallback();
}
}

});

this.streamDeck.on('error', error => {
Expand Down Expand Up @@ -89,18 +103,21 @@ export class StreamDeckHID implements HID {
}
if (control.row == 1 && control.column == 0) {
StreamDeckHID.ACCEPT_BUTTON = control;
StreamDeckHID.UNMUTE_BUTTON = control;
}
if (control.row == 1 && control.column == 1) {
StreamDeckHID.REJECT_BUTTON = control;
StreamDeckHID.MUTE_BUTTON = control;
}
}
});

this.BBB_IMG = await sharp(path.resolve(__dirname, '../assets/bbb.png'))
.flatten()
.resize(StreamDeckHID.BBB_BUTTON.pixelSize.width, StreamDeckHID.BBB_BUTTON.pixelSize.height)
.raw()
.toBuffer();
this.BBB_IMG = await this.getButtonImageBuffer(StreamDeckHID.BBB_BUTTON, 'bbb.png');
this.ACCEPT_IMG = await this.getButtonImageBuffer(StreamDeckHID.ACCEPT_BUTTON, 'accept.png');
this.REJECT_IMG = await this.getButtonImageBuffer(StreamDeckHID.REJECT_BUTTON, 'reject.png');
this.LEAVE_IMG = await this.getButtonImageBuffer(StreamDeckHID.LEAVE_BUTTON, 'leave.png');
this.MUTE_IMG = await this.getButtonImageBuffer(StreamDeckHID.MUTE_BUTTON, 'mute.png');
this.UNMUTE_IMG = await this.getButtonImageBuffer(StreamDeckHID.UNMUTE_BUTTON, 'unmute.png');

this.BBB_IMG_LG = await sharp(path.resolve(__dirname, '../assets/bbb.png'))
.flatten()
Expand All @@ -114,31 +131,12 @@ export class StreamDeckHID implements HID {
)
.raw()
.toBuffer();
}

this.ACCEPT_IMG = await sharp(path.resolve(__dirname, '../assets/accept.png'))
.flatten()
.resize(
StreamDeckHID.ACCEPT_BUTTON.pixelSize.width,
StreamDeckHID.ACCEPT_BUTTON.pixelSize.height,
)
.raw()
.toBuffer();

this.REJECT_IMG = await sharp(path.resolve(__dirname, '../assets/reject.png'))
.flatten()
.resize(
StreamDeckHID.REJECT_BUTTON.pixelSize.width,
StreamDeckHID.REJECT_BUTTON.pixelSize.height,
)
.raw()
.toBuffer();

this.LEAVE_IMG = await sharp(path.resolve(__dirname, '../assets/leave.png'))
async getButtonImageBuffer(button: StreamDeckButtonControlDefinitionLcdFeedback, image: string): Promise<Buffer> {
return sharp(path.resolve(__dirname, '../assets/'+image))
.flatten()
.resize(
StreamDeckHID.LEAVE_BUTTON.pixelSize.width,
StreamDeckHID.LEAVE_BUTTON.pixelSize.height,
)
.resize(button.pixelSize.width, button.pixelSize.height)
.raw()
.toBuffer();
}
Expand Down Expand Up @@ -185,14 +183,18 @@ export class StreamDeckHID implements HID {
await this.streamDeck.close();
}

connected(leave: () => void): void {
connected(actions: HIDActions): void {
this.streamDeck.clearPanel();

this.streamDeck.fillKeyBuffer(StreamDeckHID.BBB_BUTTON.index, this.BBB_IMG);
this.streamDeck.fillKeyBuffer(StreamDeckHID.MUTE_BUTTON.index, this.MUTE_IMG);
this.streamDeck.fillKeyBuffer(StreamDeckHID.UNMUTE_BUTTON.index, this.UNMUTE_IMG);
this.streamDeck.fillKeyBuffer(StreamDeckHID.LEAVE_BUTTON.index, this.LEAVE_IMG);

this.isConnected = true;
this.leaveCallback = leave;
this.muteCallback = actions.mute;
this.unmuteCallback = actions.unmute;
this.leaveCallback = actions.leave;
}

disconnected(): void {
Expand Down
1 change: 1 addition & 0 deletions appliance-application/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"auto_reject_time": 10,
"preferred_pin_screen": "Screen left",
"hide_close_button": false,
"keyboard_hid": false,
"debug": false,
"room": {
"name": "Name of the room",
Expand Down