Skip to content
This repository was archived by the owner on Dec 17, 2022. It is now read-only.

Commit 17ff8c5

Browse files
committed
Make Stream Deck display feedback for active DVE source, program camera, and also if camera+dve is active.
Moving the feedback config into the Stream Deck config section. Making sure only changes are sent to the Stream Deck. Only downside: right now, flashing buttons (alternating on/off) is not supported. There are certainly missing states which are not updating correctly. But at the moment I don't care cause it works for me.
1 parent 7ec6353 commit 17ff8c5

File tree

3 files changed

+134
-70
lines changed

3 files changed

+134
-70
lines changed

ControlThemAll.js

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import got from 'got';
44
import delay from 'delay';
55
import merge from 'deepmerge';
66
import {Enums} from 'atem-connection';
7-
// Import { EventEmitter } from 'inf-ee'
87

98
import beforeShutdown from './beforeShutdown.js';
109
import {ControllerWebServer} from './ControllerWebServer.js';
@@ -167,7 +166,7 @@ export class ControlThemAll {
167166

168167
streamDeckOnDisconnect(parameters) {
169168
console.log('streamDeck onDisconnect:', parameters);
170-
// This.streamDeckStopTimers();
169+
this.streamDeckStopTimers();
171170
}
172171

173172
streamDeckOnButtonDown(keyIndex) {
@@ -417,6 +416,7 @@ export class ControlThemAll {
417416

418417
this.updateDveButtons();
419418
this.controllerMidi.updateButtonsViaState(this.config.buttons);
419+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
420420
},
421421

422422
switchProgramAndDveSource: () => {
@@ -565,6 +565,7 @@ export class ControlThemAll {
565565
}
566566

567567
this.controllerMidi.updateButtonsViaState(this.config.buttons);
568+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
568569
},
569570
ResetDvePosition: (options, value) => {
570571
const {defaultValue} = options;
@@ -594,6 +595,7 @@ export class ControlThemAll {
594595
}
595596

596597
this.controllerMidi.updateButtonsViaState(this.config.buttons);
598+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
597599
},
598600
ResetDveMask: (options, value) => {
599601
const {defaultValue} = options;
@@ -623,6 +625,7 @@ export class ControlThemAll {
623625
}
624626

625627
this.controllerMidi.updateButtonsViaState(this.config.buttons);
628+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
626629
},
627630

628631
ResetDveAll: () => {
@@ -669,6 +672,7 @@ export class ControlThemAll {
669672

670673
this.controllerMidi.updateButtonsViaState(this.config.buttons);
671674
this.controllerMidi.updateControllersViaState(this.config.controllers);
675+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
672676
},
673677

674678
FadeToBlack: (options, value) => this.controllerAtem.fadeToBlack(),
@@ -682,6 +686,7 @@ export class ControlThemAll {
682686
}
683687

684688
this.controllerMidi.updateButtonsViaState(this.config.buttons);
689+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
685690
},
686691

687692
ChangeProgramSource: (options, value) => {
@@ -737,6 +742,10 @@ export class ControlThemAll {
737742
if (this.controllerMidi) {
738743
this.controllerMidi.updateButtonsViaState(this.config.buttons);
739744
}
745+
746+
if (this.controllerStreamDeck) {
747+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
748+
}
740749
},
741750
ChangeDvePosition: (options, value) => {
742751
const {defaultValue} = options;
@@ -774,6 +783,10 @@ export class ControlThemAll {
774783
if (this.controllerMidi) {
775784
this.controllerMidi.updateButtonsViaState(this.config.buttons);
776785
}
786+
787+
if (this.controllerStreamDeck) {
788+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
789+
}
777790
},
778791
ChangeDveMask: (options, value) => {
779792
const {defaultValue} = options;
@@ -809,6 +822,10 @@ export class ControlThemAll {
809822
if (this.controllerMidi) {
810823
this.controllerMidi.updateButtonsViaState(this.config.buttons);
811824
}
825+
826+
if (this.controllerStreamDeck) {
827+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons);
828+
}
812829
},
813830

814831
ChangeAudioSourceGain: (options, value) => this.getActionChain('changeAudioSourceGain')(options, value),
@@ -837,26 +854,40 @@ export class ControlThemAll {
837854
this.updateButtonState(btns, {state: 'noteoff', value: 0}, 'note');
838855
}
839856

840-
// SwitchStreamDeckOn(btns) {
841-
// btns = asArray(btns)
842-
// // Console.log(`switchStreamDeckOn:`, btns)
843-
// btns = btns.map((btn) => ({ note: btn }))
844-
// this.updateStreamDeckState(btns, { state: 'buttonon', value: 127 }, 'note')
845-
// }
846-
847-
// switchStreamDeckFlashing(btns) {
848-
// btns = asArray(btns)
849-
// // Console.log(`switchStreamDeckFlashing:`, btns)
850-
// btns = btns.map((btn) => ({ note: btn }))
851-
// this.updateStreamDeckState(btns, { state: 'flashingon', value: 127 }, 'note')
852-
// }
853-
854-
// switchStreamDeckOff(btns) {
855-
// btns = asArray(btns)
856-
// // Console.log(`switchStreamDeckOff:`, btns)
857-
// btns = btns.map((btn) => ({ note: btn }))
858-
// this.updateStreamDeckState(btns, { state: 'buttonoff', value: 0 }, 'note')
859-
// }
857+
switchStreamDeckOn(btns) {
858+
btns = asArray(btns);
859+
console.log('switchStreamDeckOn:', btns);
860+
btns = btns.map(btn => ({button: btn}));
861+
this.updateStreamDeckState(btns, {state: 'buttonon'}, 'button');
862+
}
863+
864+
switchStreamDeckFlashing(btns) {
865+
btns = asArray(btns);
866+
console.log('switchStreamDeckFlashing:', btns);
867+
btns = btns.map(btn => ({button: btn}));
868+
this.updateStreamDeckState(btns, {state: 'flashingon'}, 'button');
869+
}
870+
871+
switchStreamDeckOff(btns) {
872+
btns = asArray(btns);
873+
console.log('switchStreamDeckOff:', btns);
874+
btns = btns.map(btn => ({button: btn}));
875+
this.updateStreamDeckState(btns, {state: 'buttonoff'}, 'button');
876+
}
877+
878+
updateStreamDeckState(buttonStates, overwrite = {}, via = 'action') {
879+
buttonStates = asArray(buttonStates);
880+
buttonStates = buttonStates.map(buttonState => ({state: 'buttonoff', ...buttonState, ...overwrite}));
881+
this.config.streamDeck.buttons = this.config.streamDeck.buttons.map(button => {
882+
const updatedButtonState = find(buttonStates, element => element[via] === button[via]);
883+
if (updatedButtonState) {
884+
button.value = updatedButtonState.value;
885+
button.state = updatedButtonState.state;
886+
}
887+
888+
return button;
889+
});
890+
}
860891

861892
getButtonsByAction(action) {
862893
return this.config.buttons.filter(element => element.action === action || element.noteOn?.action === action || element.noteOff?.action === action).map(element => element.note);
@@ -903,14 +934,14 @@ export class ControlThemAll {
903934
const buttonsForDveSelection = this.config.feedback.buttonsForActiveUpstreamKeyerFillSource;
904935
const buttonActiveDveFillSource = buttonsForDveSelection[nameOfInput];
905936

906-
const streamDecksForDveSelection = this.config.feedback.streamDeckForActiveUpstreamKeyerFillSource;
937+
const streamDecksForDveSelection = this.config.streamDeck.feedback.activeUpstreamKeyerFillSource;
907938
const streamDeckActiveDveFillSource = streamDecksForDveSelection[nameOfInput];
908939

909940
this.switchButtonLightOff(difference(flatten(Object.values(buttonsForDveSelection)), buttonActiveDveFillSource));
910941
this.switchButtonLightOn(buttonActiveDveFillSource);
911942

912-
// This.switchStreamDeckOff(difference(flatten(Object.values(streamDecksForDveSelection)), streamDeckActiveDveFillSource))
913-
// this.switchStreamDeckOn(streamDeckActiveDveFillSource)
943+
this.switchStreamDeckOff(difference(flatten(Object.values(streamDecksForDveSelection)), streamDeckActiveDveFillSource));
944+
this.switchStreamDeckOn(streamDeckActiveDveFillSource);
914945
}
915946

916947
updatecontrollerState(controllerStates, overwrite = {}, via = 'action') {
@@ -973,8 +1004,9 @@ export class ControlThemAll {
9731004
console.log('pathToChange:', pathToChange);
9741005
}
9751006

1007+
const isInitial = pathToChange.length === 1 && pathToChange[0] === 'initial';
1008+
9761009
for (const path of pathToChange) {
977-
const isInitial = path === 'initial';
9781010
if (isInitial) {
9791011
// Find all ChangeAudioSourceGain controllers and set them to their default
9801012
for (const controller of this.config.controllers) {
@@ -1028,8 +1060,8 @@ export class ControlThemAll {
10281060
const buttonsForProgramInputWithoutDve = this.config.feedback.buttonsForProgramInputWithoutDve;
10291061
const buttonsForProgramInputWithDve = this.config.feedback.buttonsForProgramInputWithDve;
10301062

1031-
const streamDeckForProgramInputWithoutDve = this.config.feedback.streamDeckForProgramInputWithoutDve;
1032-
const streamDeckForProgramInputWithDve = this.config.feedback.streamDeckForProgramInputWithDve;
1063+
const streamDeckForProgramInputWithoutDve = this.config.streamDeck.feedback.programInputWithoutDve;
1064+
const streamDeckForProgramInputWithDve = this.config.streamDeck.feedback.programInputWithDve;
10331065

10341066
const nameOfInput = Object.keys(this.config.inputMapping).find(key => this.config.inputMapping[key] === programInput);
10351067

@@ -1042,6 +1074,9 @@ export class ControlThemAll {
10421074
this.switchButtonLightOff(difference(buttonsForProgramInputAll, buttonsForProgramInput));
10431075
this.switchButtonLightOn(buttonsForProgramInput);
10441076

1077+
this.switchStreamDeckOff(difference(streamDeckForProgramInputAll, streamDeckForProgramInput));
1078+
this.switchStreamDeckOn(streamDeckForProgramInput);
1079+
10451080
this.updateDveButtons();
10461081

10471082
if (state.video.mixEffects[me].fadeToBlack.isFullyBlack) {
@@ -1054,15 +1089,12 @@ export class ControlThemAll {
10541089

10551090
if (this.controllerMidi) {
10561091
this.controllerMidi.updateControllersViaState(this.config.controllers);
1057-
}
1058-
1059-
if (this.controllerMidi) {
10601092
this.controllerMidi.updateButtonsViaState(this.config.buttons);
10611093
}
10621094

1063-
// If (this.controllerStreamDeck) {
1064-
// this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons)
1065-
// }
1095+
if (this.controllerStreamDeck) {
1096+
this.controllerStreamDeck.updateButtonsViaState(this.config.streamDeck.buttons, isInitial);
1097+
}
10661098
}
10671099

10681100
async exitHandler(options, exitCode) {

ControllerStreamDeck.js

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import {listStreamDecks, openStreamDeck} from '@elgato-stream-deck/node';
22
import debug from 'debug';
33
import {throttle} from 'throttle-debounce';
44
import {EventEmitter} from 'inf-ee';
5+
import {cloneDeep} from 'lodash-es';
6+
import {diffByHash} from './utils.js';
57

68
import RenderButton from './RenderButton.js';
79

810
const log = debug('sio:sdeck');
911

1012
const THROTTLE_BUTTON_UPDATE = 250; // Ms
11-
const THROTTLE_CONTROLLER_UPDATE = 250; // Ms
1213

1314
export const ConnectionState = {
1415
Closed: 0x00,
@@ -29,6 +30,7 @@ export class ControllerStreamDeck extends EventEmitter {
2930
this._model = undefined;
3031
this._path = undefined;
3132
this._serialNumber = undefined;
33+
this.buttonStates = [];
3234

3335
this.sessionId = -1;
3436

@@ -73,8 +75,6 @@ export class ControllerStreamDeck extends EventEmitter {
7375
// Clear all keys
7476
await this._streamDeck.clearPanel();
7577

76-
await this.updateButtonsViaStateInstant(this.config.streamDeck.buttons);
77-
7878
this.sessionId += 1;
7979
this.emit('connect', {
8080
isReconnect: this.sessionId > 0,
@@ -110,41 +110,72 @@ export class ControllerStreamDeck extends EventEmitter {
110110
}
111111
}
112112

113-
// SendButtonOff(options) {
114-
// this.send('noteoff', options);
115-
// }
116-
117-
// sendButtonOn(options) {
118-
// this.send('noteon', options);
119-
// }
120-
121113
updateButtonsViaStateInstant(buttonStates) {
114+
this.log('debug', 'updateButtonsViaStateInstant');
122115
return Promise.all(buttonStates.map(async btn => {
123116
let state = (btn.state || 'buttonoff').toLowerCase();
124117
if (['flashingon', 'flashingoff'].includes(state)) {
125118
btn.state = state === 'flashingon' ? 'flashingoff' : 'flashingon';
126119
state = state === 'flashingon' ? 'buttonon' : 'buttonoff';
127120
}
128121

122+
switch (state) {
123+
case 'buttonon': {
124+
state = '#ff0000';
125+
126+
break;
127+
}
128+
129+
case 'flashingon': {
130+
state = '#00ff00';
131+
132+
break;
133+
}
134+
135+
case 'flashingoff': {
136+
state = '#0000ff';
137+
138+
break;
139+
}
140+
141+
default: {
142+
state = '#000000';
143+
}
144+
}
145+
129146
await this.send({
130147
keyIndex: btn.button,
131148
label: btn.label,
132-
backgroundColor: state === 'buttonon' ? '#FF0000' : '#000000',
149+
backgroundColor: state || '#000000',
133150
});
134-
}));
151+
}),
152+
);
153+
}
154+
155+
async updateButtonsViaStateCached(buttonStates) {
156+
this.log('debug', 'updateButtonsViaStateCached');
157+
// TODO: by using diffByHash I am ignoring all those buttons whish should be "flashing" since they will not change by themselves. But that's ok for now.
158+
console.log(buttonStates[0]);
159+
console.log(this.buttonStates[0]);
160+
const buttonStatesChanged = diffByHash(buttonStates, this.buttonStates);
161+
console.log('buttonStatesChanged', buttonStatesChanged);
162+
if (buttonStatesChanged.length > 0) {
163+
await this.updateButtonsViaStateInstant(buttonStatesChanged);
164+
this.buttonStates = cloneDeep(buttonStates);
165+
}
135166
}
136167

137168
updateButtonsViaState(buttonStates, instant = false) {
138169
// This.log('debug', 'updateButtonsViaState')
139170
if (instant) {
140-
// This.log('debug', 'updateButtonsViaStateInstant')
171+
this.log('debug', 'updateButtonsViaStateInstant');
141172
this.updateButtonsViaStateInstant(buttonStates);
142173
} else {
143174
if (!this.updateButtonsViaStateThrottled) {
144-
this.updateButtonsViaStateThrottled = throttle(THROTTLE_BUTTON_UPDATE, false, buttonStates =>
145-
// This.log('debug', 'updateButtonsViaStateThrottled')
146-
this.updateButtonsViaStateInstant(buttonStates),
147-
);
175+
this.updateButtonsViaStateThrottled = throttle(THROTTLE_BUTTON_UPDATE, false, buttonStates => {
176+
this.log('debug', 'updateButtonsViaStateThrottled');
177+
this.updateButtonsViaStateCached(buttonStates);
178+
});
148179
}
149180

150181
this.updateButtonsViaStateThrottled(buttonStates);

config.default.yaml

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ streamDeck:
1313
enabled: false
1414
model: ''
1515
serialNumber: ''
16+
feedback:
17+
programInputWithoutDve:
18+
cam1: [10]
19+
cam2: [6]
20+
cam3: [7]
21+
cam4: [8]
22+
23+
programInputWithDve:
24+
cam1: [5]
25+
cam2: [11]
26+
cam3: [12]
27+
cam4: [13]
28+
29+
activeUpstreamKeyerFillSource:
30+
cam1: [0]
31+
cam2: [1]
32+
cam3: [2]
33+
cam4: [3]
34+
1635
buttons:
1736
- button: 0
1837
label: 'DVE Cam1'
@@ -750,21 +769,3 @@ feedback:
750769
cam2: [21, 47]
751770
cam3: [22, 45]
752771
cam4: [23, 46]
753-
754-
streamDeckForProgramInputWithoutDve:
755-
cam1: [16]
756-
cam2: [9]
757-
cam3: [10]
758-
cam4: [11]
759-
760-
streamDeckForProgramInputWithDve:
761-
cam1: [8]
762-
cam2: [17]
763-
cam3: [18]
764-
cam4: [19]
765-
766-
streamDeckForActiveUpstreamKeyerFillSource:
767-
cam1: [20]
768-
cam2: [21]
769-
cam3: [22]
770-
cam4: [23]

0 commit comments

Comments
 (0)