From 2707839c2d1123e383c7bfdbfed5400f27a95dc4 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Wed, 9 Sep 2026 17:23:39 -0700 Subject: [PATCH 1/2] feat: [GH-159] Add Elgato Master Mix Control --- .../integration/wavelink/WaveLinkIconHandler.java | 10 ++++++++++ .../integration/wavelink/WaveLinkMuteResolver.java | 1 + .../wavelink/command/CommandWaveLinkChangeLevel.java | 1 + .../wavelink/command/CommandWaveLinkChangeMute.java | 6 +++++- .../wavelink/command/WaveLinkCommandTarget.java | 2 +- src/main/java/dev/niels/wavelink/IWaveLinkClient.java | 8 ++++++++ .../webui/src/app/features/commands/command-catalog.ts | 4 ++-- .../app/features/commands/command-fields.component.ts | 2 ++ .../webui/src/app/models/generated/backend.types.ts | 2 +- 9 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkIconHandler.java b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkIconHandler.java index cbbb642d..a2e660e8 100644 --- a/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkIconHandler.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkIconHandler.java @@ -15,6 +15,7 @@ import dev.niels.wavelink.impl.model.WaveLinkChannel; import dev.niels.wavelink.impl.model.WaveLinkImage; +import dev.niels.wavelink.impl.model.WaveLinkMix; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.extern.log4j.Log4j2; @@ -53,6 +54,9 @@ private Optional fromChange(CommandWaveLinkChange change) { case Channel, Mix -> { return channelImage(change.getId1()); } + case MixMaster -> { + return mixImage(change.getId1()); + } case Output -> { return Optional.of(outputImage()); } @@ -66,6 +70,12 @@ private Optional channelImage(@Nullable String id) { .map(WaveLinkImage::getImage); } + private Optional mixImage(@Nullable String id) { + return Optional.ofNullable(waveLinkService.getMixes().get(id)) + .map(WaveLinkMix::image) + .map(WaveLinkImage::getImage); + } + private BufferedImage outputImage() { return iconService.DEVICE; } diff --git a/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolver.java b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolver.java index 0c594bbf..2ac6e3d6 100644 --- a/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolver.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/WaveLinkMuteResolver.java @@ -52,6 +52,7 @@ public Optional resolve(Commands command, String target) { var mix = channel.findMix(cmd.getId2()).orElseGet(() -> waveLink.getMixFromId(cmd.getId2())); yield Optional.ofNullable(mix.isMuted()); } + case MixMaster -> Optional.ofNullable(waveLink.getMixFromId(cmd.getId1()).isMuted()); case Output -> { var output = waveLink.getOutputFromId(cmd.getId1()); yield output.outputs() == null || output.outputs().isEmpty() diff --git a/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeLevel.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeLevel.java index 1501de83..238df9a7 100644 --- a/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeLevel.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeLevel.java @@ -56,6 +56,7 @@ public void execute(DialActionParameters context) { case Input -> service.setInputLevel(getId1(), value); case Channel -> service.setChannelLevel(getId1(), value); case Mix -> service.setChannelLevel(getId1(), getId2(), value); + case MixMaster -> service.setMixLevel(getId1(), value); case Output -> service.setOutputLevel(getId1(), value); } } diff --git a/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java index cfc3585b..2b477d2d 100644 --- a/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java @@ -63,7 +63,7 @@ public void execute() { var channel = service.getChannelFromId(getId1()); service.setChannelMute(getId1(), muteType.convert(channel.isMuted())); } - case Mix -> { + case Mix -> { // Control the channel-mix combo. if (StringUtils.isBlank(getId2())) { log.warn("No mix id specified"); return; @@ -72,6 +72,10 @@ public void execute() { var mix = channel.findMix(getId2()).orElseGet(() -> service.getMixFromId(getId2())); service.setChannelMute(channel, mix, muteType.convert(mix.isMuted())); } + case MixMaster -> { // Master mix control. + var mix = service.getMixFromId(getId1()); + service.setMixMute(mix, muteType.convert(mix.isMuted())); + } case Output -> { var output = service.getOutputFromId(getId1()); if (!(output.outputs() == null || output.outputs().isEmpty())) { diff --git a/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandTarget.java b/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandTarget.java index 7b6113fa..1f1f4017 100644 --- a/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandTarget.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/WaveLinkCommandTarget.java @@ -1,5 +1,5 @@ package com.getpcpanel.integration.wavelink.command; public enum WaveLinkCommandTarget { - Input, Channel, Mix, Output + Input, Channel, Mix, Output, MixMaster } diff --git a/src/main/java/dev/niels/wavelink/IWaveLinkClient.java b/src/main/java/dev/niels/wavelink/IWaveLinkClient.java index 9d16799a..e6a26cca 100644 --- a/src/main/java/dev/niels/wavelink/IWaveLinkClient.java +++ b/src/main/java/dev/niels/wavelink/IWaveLinkClient.java @@ -139,10 +139,18 @@ default void addCurrentToChannel(String channelId) { void setChannelAudioEffect(WaveLinkChannel channel, WaveLinkEffect effect); + default void setMixLevel(String mixId, double value) { + setMixLevel(getMixFromId(mixId), value); + } + default void setMixLevel(WaveLinkMix mix, double value) { setMix(mix, value, null); } + default void setMixMute(String mixId, boolean mute) { + setMixMute(getMixFromId(mixId), mute); + } + default void setMixMute(WaveLinkMix mix, boolean mute) { setMix(mix, null, mute); } diff --git a/src/main/webui/src/app/features/commands/command-catalog.ts b/src/main/webui/src/app/features/commands/command-catalog.ts index 74e83183..23bfb668 100644 --- a/src/main/webui/src/app/features/commands/command-catalog.ts +++ b/src/main/webui/src/app/features/commands/command-catalog.ts @@ -288,7 +288,7 @@ const FIELD_DEFS: FieldDef_[] = [ { kind: 'select', key: 'commandType', label: 'Target', options: [ { value: 'Channel', label: 'Channel' }, { value: 'Input', label: 'Input' }, - { value: 'Mix', label: 'Mix' }, { value: 'Output', label: 'Output' }, + { value: 'Mix', label: 'Mix' }, { value: 'MixMaster', label: 'Master Mix' }, { value: 'Output', label: 'Output' }, ], }, { kind: 'wavelink-target' }, @@ -301,7 +301,7 @@ const FIELD_DEFS: FieldDef_[] = [ { kind: 'select', key: 'commandType', label: 'Target', options: [ { value: 'Channel', label: 'Channel' }, { value: 'Input', label: 'Input' }, - { value: 'Mix', label: 'Mix' }, { value: 'Output', label: 'Output' }, + { value: 'Mix', label: 'Mix' }, { value: 'MixMaster', label: 'Master Mix' }, { value: 'Output', label: 'Output' }, ], }, { kind: 'wavelink-target' }, diff --git a/src/main/webui/src/app/features/commands/command-fields.component.ts b/src/main/webui/src/app/features/commands/command-fields.component.ts index 5696f210..aa98a9a9 100644 --- a/src/main/webui/src/app/features/commands/command-fields.component.ts +++ b/src/main/webui/src/app/features/commands/command-fields.component.ts @@ -431,6 +431,7 @@ export class CommandFieldsComponent { switch (this.val('commandType')) { case 'Input': return 'wl-inputs'; case 'Output': return 'wl-outputs'; + case 'MixMaster': return 'wl-mixes'; default: return 'wl-channels'; // Channel and Mix both pick a channel first } } @@ -438,6 +439,7 @@ export class CommandFieldsComponent { switch (this.val('commandType')) { case 'Input': return 'Input'; case 'Output': return 'Output'; + case 'MixMaster': return 'Master Mix'; default: return 'Channel'; } } diff --git a/src/main/webui/src/app/models/generated/backend.types.ts b/src/main/webui/src/app/models/generated/backend.types.ts index 97b1f1f1..9331bb94 100644 --- a/src/main/webui/src/app/models/generated/backend.types.ts +++ b/src/main/webui/src/app/models/generated/backend.types.ts @@ -1081,6 +1081,6 @@ export type SINGLE_SLIDER_MODE = "NONE" | "STATIC" | "STATIC_GRADIENT" | "VOLUME export type VolumeButton = "mute" | "next" | "prev" | "stop" | "playPause"; -export type WaveLinkCommandTarget = "Input" | "Channel" | "Mix" | "Output"; +export type WaveLinkCommandTarget = "Input" | "Channel" | "Mix" | "Output" | "MixMaster"; export type WsEventUnion = WsAssignmentChangedEvent | WsButtonEvent | WsDeviceConnectedEvent | WsDeviceDisconnectedEvent | WsDeviceRenamedEvent | WsKnobEvent | WsLightingChangedEvent | WsProfileSwitchedEvent | WsVisualColorsChangedEvent | DeviceSnapshotDto | WsControlSettingChangedEvent | WsNewVersionAvailableEvent; From 236066cf851db4edab3f7b60e2367cd3e2a3625e Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Wed, 9 Sep 2026 17:45:29 -0700 Subject: [PATCH 2/2] feat: [GH-159] Update Comments + Add Blank String Check to ChangeMute Command --- .../wavelink/command/CommandWaveLinkChangeMute.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java index 2b477d2d..1e79a01e 100644 --- a/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java +++ b/src/main/java/com/getpcpanel/integration/wavelink/command/CommandWaveLinkChangeMute.java @@ -72,7 +72,12 @@ public void execute() { var mix = channel.findMix(getId2()).orElseGet(() -> service.getMixFromId(getId2())); service.setChannelMute(channel, mix, muteType.convert(mix.isMuted())); } - case MixMaster -> { // Master mix control. + case MixMaster -> { // Master mix control. Different from normal mix. + if (StringUtils.isBlank(getId1())) { + log.warn("No master mix id specified"); + return; + } + var mix = service.getMixFromId(getId1()); service.setMixMute(mix, muteType.convert(mix.isMuted())); }