Skip to content

Commit 03f0aac

Browse files
authored
Merge pull request #52 from bitfocus/improve-show-control
Improve show control
2 parents 6a5ca7d + a3d42b6 commit 03f0aac

File tree

7 files changed

+78
-139
lines changed

7 files changed

+78
-139
lines changed

src/actions/control.ts

+38-50
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CompanionActionDefinitions } from '@companion-module/base'
1+
import { CompanionActionDefinitions, CompanionOptionValues } from '@companion-module/base'
22
import { CompanionActionWithCallback } from './common.js'
33
import { InstanceBaseExt } from '../types.js'
44
import { WingConfig } from '../config.js'
@@ -9,84 +9,72 @@ import { getIdLabelPair } from '../choices/utils.js'
99

1010
export enum OtherActionId {
1111
RecallScene = 'recall-scene',
12-
RecallSceneFromList = 'recall-scene-from-list',
1312
SendLibraryAction = 'send-library-action',
1413
}
1514

1615
export function createControlActions(self: InstanceBaseExt<WingConfig>): CompanionActionDefinitions {
1716
const send = self.sendCommand
1817
const state = self.state
1918
const ensureLoaded = self.ensureLoaded
19+
const subscriptions = self.subscriptions
2020

2121
const actions: { [id in OtherActionId]: CompanionActionWithCallback | undefined } = {
2222
[OtherActionId.RecallScene]: {
2323
name: 'Recall Scene',
24-
description: 'Recall a scene and optionally go to it',
24+
description:
25+
'ATTENTION: if you have the same scene name twice in your show, you will not be able to recall it by name! In this case, use the scene ID instead.',
2526
options: [
27+
{
28+
type: 'checkbox',
29+
id: 'useSceneId',
30+
label: 'Use Scene Id',
31+
default: false,
32+
},
2633
{
2734
type: 'number',
28-
id: 'num',
35+
id: 'sceneId',
2936
label: 'Scene Number',
3037
min: 1,
3138
max: 16384,
3239
default: 1,
40+
isVisible: (options: CompanionOptionValues): boolean => {
41+
return options.useSceneId != null && options.useSceneId == true
42+
},
3343
},
3444
{
35-
type: 'checkbox',
36-
id: 'go',
37-
label: 'Go to Scene?',
38-
default: true,
45+
...GetDropdown('Scene Name', 'sceneName', state.namedChoices.scenes),
46+
isVisible: (options: CompanionOptionValues): boolean => {
47+
return options.useSceneId != null && options.useSceneId == false
48+
},
3949
},
4050
],
4151
callback: async (event) => {
42-
send(ControlCommands.LibrarySceneSelectionIndex(), event.options.num as number)
43-
if (event.options.go) {
44-
send(ControlCommands.LibraryAction(), 'GO')
52+
const useSceneId = event.options.useSceneId
53+
let sceneId = 0
54+
if (useSceneId == true) {
55+
sceneId = event.options.sceneId as number
56+
} else {
57+
sceneId = state.sceneNameToIdMap.get(event.options.sceneName as string) ?? 0
4558
}
59+
send(ControlCommands.LibrarySceneSelectionIndex(), sceneId)
60+
send(ControlCommands.LibraryAction(), 'GO')
4661
},
47-
subscribe: () => {
48-
const cmd = ControlCommands.LibraryActiveSceneIndex()
49-
ensureLoaded(cmd)
50-
},
51-
learn: () => {
52-
const cmd = ControlCommands.LibraryActiveSceneIndex()
53-
return { num: StateUtil.getNumberFromState(cmd, state) }
54-
},
55-
},
56-
[OtherActionId.RecallSceneFromList]: {
57-
name: 'Recall Scene from List',
58-
description:
59-
'Recall a scene from a list and optionally go to it (NOTE: When you add/remove/move scenes in your show, you must update this command.)',
60-
options: [
61-
GetDropdown(
62-
'Scene',
63-
'num',
64-
state.namedChoices.scenes,
65-
undefined,
66-
'This uses the index of the scene, and changes when scenes are added or removed.',
67-
),
68-
{
69-
type: 'checkbox',
70-
id: 'go',
71-
label: 'Go to Scene?',
72-
default: true,
73-
},
74-
],
75-
callback: async (event) => {
76-
const go = event.options.go as boolean
77-
send(ControlCommands.LibrarySceneSelectionIndex(), event.options.num as number)
78-
if (go) {
79-
send(ControlCommands.LibraryAction(), 'GO')
62+
subscribe: (event) => {
63+
if (event.options.useSceneId == false) {
64+
subscriptions.subscribePoll(ControlCommands.LibraryScenes())
8065
}
81-
},
82-
subscribe: () => {
83-
const cmd = ControlCommands.LibraryActiveSceneIndex()
84-
ensureLoaded(cmd)
66+
ensureLoaded(ControlCommands.LibraryActiveSceneIndex())
8567
ensureLoaded(ControlCommands.LibraryNode(), '?')
8668
},
87-
learn: () => {
69+
learn: (event) => {
70+
const sceneIdMap = state.sceneNameToIdMap
8871
const cmd = ControlCommands.LibraryActiveSceneIndex()
89-
return { num: StateUtil.getNumberFromState(cmd, state) }
72+
const sceneId = StateUtil.getNumberFromState(cmd, state)
73+
let sceneName = ''
74+
for (const [key, value] of sceneIdMap.entries()) {
75+
if (value === sceneId) sceneName = key
76+
}
77+
return { sceneName: sceneName, sceneId: sceneId, useSceneId: event.options.useSceneId }
9078
},
9179
},
9280
[OtherActionId.SendLibraryAction]: {

src/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { WingDeviceDetectorInstance } from './device-detector.js'
33
// import { ModelChoices, WingModel } from './models/types.js'
44

55
export const fadeUpdateRateDefault = 50
6-
export const pollUpdateRateDefault = 500
6+
export const pollUpdateRateDefault = 3000
77

88
export const DeskTypes = [
99
{ id: 'wing', label: 'Wing' },

src/feedbacks.ts

+1-58
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,7 @@ import {
1111
CompanionFeedbackInfo,
1212
} from '@companion-module/base'
1313
// import { compareNumber, GetDropdownFeedback, GetNumberComparator, GetPanoramaSliderFeedback } from './choices/common.js'
14-
import {
15-
GetDropdown,
16-
GetMuteDropdown,
17-
getTimeFormatChoices,
18-
getTriStateColor,
19-
getTriStateTextColor,
20-
} from './choices/common.js'
14+
import { GetDropdown, GetMuteDropdown, getTriStateColor, getTriStateTextColor } from './choices/common.js'
2115
import { getNodeNumber } from './actions/utils.js'
2216
import { StateUtil } from './state/index.js'
2317
import { UsbPlayerCommands } from './commands/usbplayer.js'
@@ -34,9 +28,7 @@ export enum FeedbackId {
3428
Mute = 'mute',
3529
SendMute = 'send-mute',
3630
AesStatus = 'aes-status',
37-
RecorderTime = 'recorder-time',
3831
RecorderState = 'recorder-state',
39-
PlayerTime = 'player-time',
4032
}
4133

4234
function subscribeFeedback(
@@ -160,33 +152,6 @@ export function GetFeedbacksList(
160152
unsubscribeFeedback(subs, cmd, event)
161153
},
162154
},
163-
[FeedbackId.RecorderTime]: {
164-
type: 'advanced',
165-
name: 'USB Recorder Time',
166-
description: 'Current Time of the Recording',
167-
options: [GetDropdown('Format', 'format', getTimeFormatChoices())],
168-
callback: (): CompanionAdvancedFeedbackResult => {
169-
const cmd = UsbPlayerCommands.RecorderTime()
170-
const time = StateUtil.getNumberFromState(cmd, state) ?? 'N/A'
171-
if (time) {
172-
if (isNaN(Number(time))) {
173-
return {
174-
text: time as string,
175-
}
176-
} else return {}
177-
} else return {}
178-
},
179-
subscribe: (event): void => {
180-
const cmd = UsbPlayerCommands.RecorderTime()
181-
subs.subscribePoll(cmd, event.id, event.feedbackId as FeedbackId)
182-
subscribeFeedback(ensureLoaded, subs, cmd, event)
183-
},
184-
unsubscribe: (event: CompanionFeedbackInfo): void => {
185-
const cmd = UsbPlayerCommands.RecorderTime()
186-
subs.unsubscribePoll(cmd, event.feedbackId as FeedbackId)
187-
unsubscribeFeedback(subs, cmd, event)
188-
},
189-
},
190155
[FeedbackId.RecorderState]: {
191156
type: 'advanced',
192157
name: 'USB Recorder State',
@@ -236,28 +201,6 @@ export function GetFeedbacksList(
236201
unsubscribeFeedback(subs, cmd, event)
237202
},
238203
},
239-
[FeedbackId.PlayerTime]: {
240-
type: 'advanced',
241-
name: 'USB Player Time',
242-
description: 'Current Time of the USB Player',
243-
options: [GetDropdown('Format', 'format', getTimeFormatChoices())],
244-
callback: (): CompanionAdvancedFeedbackResult => {
245-
const cmd = UsbPlayerCommands.PlayerTotalTime()
246-
return {
247-
text: `${StateUtil.getNumberFromState(cmd, state) ?? 'N/A'}`,
248-
}
249-
},
250-
subscribe: (event): void => {
251-
const cmd = UsbPlayerCommands.PlayerTotalTime()
252-
subs.subscribePoll(cmd, event.id, event.feedbackId as FeedbackId)
253-
subscribeFeedback(ensureLoaded, subs, cmd, event)
254-
},
255-
unsubscribe: (event: CompanionFeedbackInfo): void => {
256-
const cmd = UsbPlayerCommands.PlayerTotalTime()
257-
subs.unsubscribePoll(cmd, event.feedbackId as FeedbackId)
258-
unsubscribeFeedback(subs, cmd, event)
259-
},
260-
},
261204
}
262205

263206
return feedbacks

src/index.ts

+11-12
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
2626
state: WingState
2727
model: ModelSpec
2828
osc: osc.UDPPort
29-
30-
private WingSubscriptions: WingSubscriptions
29+
subscriptions: WingSubscriptions
3130

3231
private heartbeatTimer: NodeJS.Timeout | undefined
3332
private reconnectTimer: NodeJS.Timeout | undefined
@@ -58,7 +57,7 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
5857
super(internal)
5958

6059
this.osc = new osc.UDPPort({})
61-
this.WingSubscriptions = new WingSubscriptions()
60+
this.subscriptions = new WingSubscriptions()
6261
this.model = getDeskModel(WingModel.Full)
6362
this.state = new WingState(this.model)
6463

@@ -145,7 +144,7 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
145144
async configUpdated(config: WingConfig): Promise<void> {
146145
this.config = config
147146

148-
this.WingSubscriptions = new WingSubscriptions()
147+
this.subscriptions = new WingSubscriptions()
149148
this.state = new WingState(this.model)
150149

151150
this.transitions.stopAll()
@@ -171,7 +170,7 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
171170
}
172171

173172
updateFeedbacks(): void {
174-
this.setFeedbackDefinitions(GetFeedbacksList(this, this.state, this.WingSubscriptions, this.ensureLoaded))
173+
this.setFeedbackDefinitions(GetFeedbacksList(this, this.state, this.subscriptions, this.ensureLoaded))
175174
}
176175

177176
updateVariableDefinitions(): void {
@@ -243,7 +242,7 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
243242
}, 9000)
244243
this.statusUpdateInterval = setInterval(() => {
245244
this.requestStatusUpdates()
246-
}, this.config.statusPollUpdateRate ?? 250)
245+
}, this.config.statusPollUpdateRate ?? 3000)
247246

248247
this.state.requestNames(this.model, this.ensureLoaded)
249248
this.requestQueue.clear()
@@ -312,7 +311,7 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
312311
}
313312

314313
private requestStatusUpdates(): void {
315-
this.WingSubscriptions.getPollPaths().forEach((c) => {
314+
this.subscriptions.getPollPaths().forEach((c) => {
316315
this.sendCommand(c)
317316
})
318317
}
@@ -327,19 +326,19 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
327326

328327
// scene list
329328
if (libRe.test(msg.address)) {
330-
console.log('Got library')
331-
const content = (args[0].value as string) ?? ''
329+
const content = String(args[0]?.value ?? '')
332330
const scenes = content.match(/\$scenes\s+list\s+\[([^\]]+)\]/)
333331
console.log(scenes)
334332
if (scenes) {
335333
const sceneList = scenes[1].split(',').map((s) => s.trim())
336-
this.state.namedChoices.scenes = sceneList.map((s, i) => ({ id: i + 1, label: s }))
334+
this.state.namedChoices.scenes = sceneList.map((s) => ({ id: s, label: s }))
335+
this.state.sceneNameToIdMap = new Map(sceneList.map((s, i) => [s, i + 1]))
337336
}
338337
}
339338
}
340339

341340
private checkFeedbackChanges(msg: osc.OscMessage): void {
342-
const toUpdate = this.WingSubscriptions.getFeedbacks(msg.address)
341+
const toUpdate = this.subscriptions.getFeedbacks(msg.address)
343342
if (toUpdate.length > 0) {
344343
toUpdate.forEach((f) => this.messageFeedbacks.add(f))
345344
this.debounceMessageFeedbacks()
@@ -370,7 +369,7 @@ export class WingInstance extends InstanceBase<WingConfig> implements InstanceBa
370369

371370
this.setPresetDefinitions(GetPresets(this))
372371
this.setActionDefinitions(createActions(this))
373-
this.setFeedbackDefinitions(GetFeedbacksList(this, this.state, this.WingSubscriptions, this.ensureLoaded))
372+
this.setFeedbackDefinitions(GetFeedbacksList(this, this.state, this.subscriptions, this.ensureLoaded))
374373
this.checkFeedbacks()
375374
}
376375

src/state/state.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ export class WingState implements IStoredChannelSubject {
5353
scenes: [],
5454
}
5555

56+
sceneNameToIdMap: Map<string, number>
57+
5658
constructor(model: ModelSpec) {
5759
this.data = new Map()
5860
this.pressStorage = new Map()
5961
this.deltaStorage = new Map()
6062
this.storedChannel = 1
63+
this.sceneNameToIdMap = new Map()
6164
this.updateNames(model)
6265
}
6366

@@ -264,12 +267,10 @@ export class WingSubscriptions {
264267
public getPollPaths(): string[] {
265268
return Array.from(this.pollData)
266269
}
267-
public subscribePoll(path: string, feedbackId: string, type: FeedbackId): void {
268-
this.subscribe(path, feedbackId, type)
270+
public subscribePoll(path: string): void {
269271
this.pollData.add(path)
270272
}
271-
public unsubscribePoll(path: string, feedbackId: string): void {
272-
this.unsubscribe(path, feedbackId)
273+
public unsubscribePoll(path: string): void {
273274
this.pollData.delete(path)
274275
}
275276
}

src/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { InstanceBase } from '@companion-module/base'
22
import osc from 'osc'
33
import { WingTransitions } from './transitions.js'
4-
import { WingState } from './state/state.js'
4+
import { WingState, WingSubscriptions } from './state/state.js'
55

66
export interface InstanceBaseExt<TConfig> extends InstanceBase<TConfig> {
77
config: TConfig
88
osc: osc.UDPPort
99
transitions: WingTransitions
1010
state: WingState
11+
subscriptions: WingSubscriptions
1112
sendCommand: (cmd: string, argument?: number | string, preferFloat?: boolean) => void
1213
ensureLoaded: (path: string, arg?: string | number) => void
1314
}

0 commit comments

Comments
 (0)