diff --git a/src/actions/output.ts b/src/actions/output.ts index 1974b683..49fafa51 100644 --- a/src/actions/output.ts +++ b/src/actions/output.ts @@ -465,7 +465,7 @@ export function outputActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async (_action) => { @@ -509,7 +509,7 @@ export function outputActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async ({ options }) => { @@ -558,7 +558,7 @@ export function outputActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async ({ options }) => { diff --git a/src/actions/pan-balance.ts b/src/actions/pan-balance.ts index 358b5120..db5d387c 100644 --- a/src/actions/pan-balance.ts +++ b/src/actions/pan-balance.ts @@ -145,7 +145,7 @@ export function panBalanceActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async (action) => { @@ -224,7 +224,7 @@ export function panBalanceActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async (action) => { @@ -303,7 +303,7 @@ export function panBalanceActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async (action) => { @@ -382,7 +382,7 @@ export function panBalanceActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async (action) => { @@ -466,7 +466,7 @@ export function panBalanceActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async (action) => { @@ -551,7 +551,7 @@ export function panBalanceActions( return { ...options, - showvar: `$(${instance.options.connectionLabel}:pan_${MSB}.${LSB})`, + showvar: `$(${instance.label}:pan_${MSB}.${LSB})`, } }, subscribe: async (action) => { diff --git a/src/config.test.ts b/src/config.test.ts index 786d2b70..95e11f36 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -4,12 +4,14 @@ import { addModelOptionToConfig, configIsMissingLabel, configIsMissingModel, + configUnnecessarilySpecifiesLabel, + removeLabelOptionFromConfig, type SQInstanceConfig, } from './config.js' describe('config upgrade to specify a missing model', () => { test('config without model', () => { - const configMissingModel = { + const configMissingModel: SQInstanceConfig = { host: '127.0.0.1', level: 'LinearTaper', talkback: 0, @@ -17,7 +19,7 @@ describe('config upgrade to specify a missing model', () => { status: 'full', label: 'SQ', verbose: false, - } as SQInstanceConfig + } expect(configIsMissingModel(configMissingModel)).toBe(true) @@ -62,7 +64,7 @@ describe('config upgrade to specify a missing model', () => { describe('config upgrade to specify a missing label', () => { test('config without label', () => { - const configMissingLabel = { + const configMissingLabel: SQInstanceConfig = { host: '127.0.0.1', model: 'SQ5', level: 'LinearTaper', @@ -70,7 +72,7 @@ describe('config upgrade to specify a missing label', () => { midich: 0, status: 'full', verbose: false, - } as SQInstanceConfig + } expect(configIsMissingLabel(configMissingLabel)).toBe(true) @@ -96,3 +98,39 @@ describe('config upgrade to specify a missing label', () => { expect(configWithLabelSQ5.label).toBe('sq5') }) }) + +describe('config upgrade to remove an unnecessary label', () => { + test('config without label', () => { + const configMissingLabel: SQInstanceConfig = { + host: '127.0.0.1', + model: 'SQ5', + level: 'LinearTaper', + talkback: 0, + midich: 0, + status: 'full', + verbose: false, + } + + expect(configUnnecessarilySpecifiesLabel(configMissingLabel)).toBe(false) + }) + + test('config with label', () => { + const configWithLabel: SQInstanceConfig = { + host: '127.0.0.1', + model: 'SQ5', + level: 'LinearTaper', + talkback: 0, + midich: 0, + status: 'full', + verbose: false, + label: 'SQ', + } + + expect(configUnnecessarilySpecifiesLabel(configWithLabel)).toBe(true) + + removeLabelOptionFromConfig(configWithLabel) + + expect(configUnnecessarilySpecifiesLabel(configWithLabel)).toBe(false) + expect('label' in configWithLabel).toBe(false) + }) +}) diff --git a/src/config.ts b/src/config.ts index f856048d..03378741 100644 --- a/src/config.ts +++ b/src/config.ts @@ -40,12 +40,26 @@ export function configIsMissingLabel(config: SQInstanceConfig | null): config is } /** - * Add the 'label' option (defaulting to SQ) to a config that's missing one. + * Add the `'label'` option (defaulting to SQ) to a config that's missing one. */ export function addLabelOptionToConfig(config: SQInstanceConfig): void { config.label = DefaultConnectionLabel } +/** + * Determine whether the given instance config specifies a `'label'` property + * that is unnecessary because `InstanceBase.label` is the real deal and is + * always up-to-date. + */ +export function configUnnecessarilySpecifiesLabel(config: SQInstanceConfig | null): config is SQInstanceConfig { + return config !== null && 'label' in config +} + +/** Remove the `'label'` option from a config that has it. */ +export function removeLabelOptionFromConfig(config: SQInstanceConfig): void { + delete config.label +} + function createDefaultTalkbackChannelOption(): SomeCompanionConfigField { // The number of input channels depends on how many input channels the // user's chosen SQ model has. Currently all SQs have the same number of @@ -133,13 +147,6 @@ export function GetConfigFields(): SomeCompanionConfigField[] { { id: 'nosts', label: 'Not at startup' }, ], }, - { - type: 'textinput', - id: 'label', - label: 'Connection label (for displaying pan/balance variables correctly)', - width: 6, - default: DefaultConnectionLabel, - }, { type: 'checkbox', id: 'verbose', diff --git a/src/instance.js b/src/instance.js index 70139d9e..bc7125fb 100644 --- a/src/instance.js +++ b/src/instance.js @@ -22,6 +22,14 @@ export class sqInstance extends InstanceBase { /** @type {Mixer | null} */ mixer = null + /** + * The last label specified for this instance, or `null` if there wasn't a + * last label. + * + * @type {string | null} + */ + #lastLabel = null + /** * Construct an `sqInstance`. * @@ -113,6 +121,17 @@ export class sqInstance extends InstanceBase { this.options = newOptions if (canUpdateOptionsWithoutRestarting(oldOptions, newOptions)) { + const label = this.label + if (label !== this.#lastLabel) { + // The instance label might be altered just before + // `configUpdated` is called. The instance label is used in the + // "Learn" operation for some actions -- and it'll always be + // up-to-date in these uses. But it's also hardcoded in some + // presets, so if the label changes, we must redefine presets + // even if we don't have to restart the connection. + this.#lastLabel = label + this.setPresetDefinitions(getPresets(this, this.mixer.model)) + } return } @@ -128,6 +147,8 @@ export class sqInstance extends InstanceBase { this.initVariableDefinitions(model) this.setActionDefinitions(getActions(this, mixer, choices)) this.setFeedbackDefinitions(getFeedbacks(mixer, choices)) + + this.#lastLabel = this.label this.setPresetDefinitions(getPresets(this, model)) //this.checkVariables(); diff --git a/src/options.ts b/src/options.ts index 56a903f7..f8dc91c7 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,7 +1,7 @@ import { type ModelId, DefaultModel } from './mixer/models.js' import type { FaderLaw } from './mixer/mixer.js' import { RetrieveStatusAtStartup } from './mixer/mixer.js' -import { DefaultConnectionLabel, type SQInstanceConfig } from './config.js' +import type { SQInstanceConfig } from './config.js' import { Regex } from '@companion-module/base' /** Options information controlling the operation of a mixer instance. */ @@ -40,9 +40,6 @@ export type SQInstanceOptions = { * logging is enabled. */ verbose: boolean - - /** The label used to identify this Companion instance/connection. */ - connectionLabel: string } function toHost(host: SQInstanceConfig['host']): string | null { @@ -109,10 +106,6 @@ function toVerbose(verbose: SQInstanceConfig['verbose']): boolean { return Boolean(verbose) } -function toConnectionLabel(label: SQInstanceConfig['label']): string { - return typeof label === 'undefined' ? DefaultConnectionLabel : String(label) -} - /** Compute instance options from instance configuration info. */ export function optionsFromConfig({ // Comments indicate the expected types of the various config fields. @@ -123,7 +116,6 @@ export function optionsFromConfig({ midich, // number status, // string verbose, // boolean - label, // string }: SQInstanceConfig): SQInstanceOptions { return { host: toHost(host), @@ -133,7 +125,6 @@ export function optionsFromConfig({ midiChannel: toMidiChannel(midich), retrieveStatusAtStartup: toRetrieveStatusAtStartup(status), verbose: toVerbose(verbose), - connectionLabel: toConnectionLabel(label), } } @@ -151,7 +142,6 @@ export function noConnectionOptions(): SQInstanceOptions { midiChannel: 0, retrieveStatusAtStartup: RetrieveStatusAtStartup.Fully, verbose: false, - connectionLabel: DefaultConnectionLabel, } } @@ -202,10 +192,6 @@ export function canUpdateOptionsWithoutRestarting( // retrieval is extremely slow (particularly if the instance debug log is // open.) - // The connection label is only used to expose the variable names for - // pan/balance variables that are "Learn"ed at runtime, so don't restart for - // a label change. - // Otherwise we can update options without restarting. return true } diff --git a/src/presets.ts b/src/presets.ts index cf0f702d..cf13b450 100644 --- a/src/presets.ts +++ b/src/presets.ts @@ -198,7 +198,7 @@ export function getPresets(instance: sqInstance, model: Model): CompanionPresetD createtMuteLevel( `Mt+dB CH-${mixLabel}`, - `${channelLabel}\\n${mixLabel}\\n$(${instance.options.connectionLabel}:level_${MSB}.${LSB}) dB`, + `${channelLabel}\\n${mixLabel}\\n$(${instance.label}:level_${MSB}.${LSB}) dB`, 'MuteInputChannel', channel, mix, diff --git a/src/upgrades.ts b/src/upgrades.ts index a4b0f45c..6c996100 100644 --- a/src/upgrades.ts +++ b/src/upgrades.ts @@ -4,7 +4,14 @@ import { type CompanionUpgradeContext, EmptyUpgradeScript, } from '@companion-module/base' -import { configIsMissingLabel, configIsMissingModel, DefaultConnectionLabel, type SQInstanceConfig } from './config.js' +import { + configIsMissingLabel, + configIsMissingModel, + configUnnecessarilySpecifiesLabel, + DefaultConnectionLabel, + removeLabelOptionFromConfig, + type SQInstanceConfig, +} from './config.js' import { DefaultModel } from './mixer/models.js' import { convertOldLevelToOutputActionToSinkSpecific, @@ -165,6 +172,35 @@ function RewriteCombinedOutputPanBalanceActionsToSinkSpecificOutputPanBalanceAct return result } +/** + * This module used to have a `'label'` option, in which the user was expected + * to (re-)specify the instance label. This label was then used in the "Learn" + * operation for various actions, as well as in various preset definitions. + * + * But it turns out the instance label is accessible as `InstanceBase.label` + * which is always up-to-date, so there's no point in having the config option. + * + * This upgrade script removes the `'label'` option from configs that have it. + */ +function RemoveUnnecessaryConnectionLabel( + _context: CompanionUpgradeContext, + props: CompanionStaticUpgradeProps, +): CompanionStaticUpgradeResult { + const result: CompanionStaticUpgradeResult = { + updatedConfig: null, + updatedActions: [], + updatedFeedbacks: [], + } + + const oldConfig = props.config + if (configUnnecessarilySpecifiesLabel(oldConfig)) { + removeLabelOptionFromConfig(oldConfig) + result.updatedConfig = oldConfig + } + + return result +} + export const UpgradeScripts = [ EmptyUpgradeScript, CoalesceSceneRecallActions, @@ -172,6 +208,11 @@ export const UpgradeScripts = [ EnsureConnectionLabel, RewriteCombinedOutputLevelActionsToSinkSpecificOutputLevelActions, RewriteCombinedOutputPanBalanceActionsToSinkSpecificOutputPanBalanceActions, + // ...yes, we added the `'label'` config option above because we thought it + // was the only way to get the instance label, and now we're removing it + // because there in fact *is* a way to get that label without requiring that + // users redundantly specify it. So it goes. + RemoveUnnecessaryConnectionLabel, ] /*