Beta version 4.4

New in this version:

  • Fixed a nasty bug in the Show Control action that prevented the change of device id.
  • In JavaScript, there’s a new device object. There is currently no similar thing in midiScript. See details below.
  • In JavaScript and midiScript, there is now a RUN action that lets you start external processes (similar to LAUNCH) without stealing window focus. Processes can be launched “fire and forget” without waiting for a result, or you can have the script wait for the process to complete so it can receive the process’s response. See details below.
  • The d.ts file has been updated to include the new JavaScript definitions. You can rerun the JS folder update via the Global Settings button to install it.

Version 4.3.0.5 (link removed due to wrong version, see link in later post)

device object details

var devs = device.devices(); to get all devices
var dev = device.devices(“Name”); to get a device by name
var curdev = device.current; to get the current device for this script instance
var dev = device[0]; to access a device by index

From the d.ts file:

/**
 * Device host API.
 *
 * Supports:
 * - `device.devices()` to get all devices
 * - `device.devices("Name")` to get a device by name
 * - `device.current` to get the current device for this script instance
 * - `device[0]` to access a device by index
 */
interface DeviceApi {
	/**
	 * Gets all known Stream Deck devices.
	 * @returns Array of known devices.
	 */
	devices(): DeviceInfo[];

	/**
	 * Gets the first known device with the specified name.
	 * Name matching is case-insensitive.
	 * @param name Device name as reported by Stream Deck.
	 * @returns The matching device, or `null` if no match was found.
	 */
	devices(name: string): DeviceInfo | null;

	/**
	 * Gets the device associated with the current script instance.
	 * Includes the action instance `row` and `column`.
	 */
	readonly current: CurrentDeviceInfo | null;

	/**
	 * Gets a device by zero-based index.
	 * Returns `null` if the index is out of range.
	 */
	[index: number]: DeviceInfo | null;
}

/**
 * Basic Stream Deck device information returned by `device.devices()`,
 * `device.devices(name)`, and indexed access such as `device[0]`.
 */
interface DeviceInfo {
	/** Unique Stream Deck device id/UUID. */
	id: string;

	/** User-visible device name reported by Stream Deck. */
	name: string;

	/** Device type name, for example `StreamDeckXL` or `StreamDeckPlus`. */
	type: string;

	/** Numeric device type id from the native enum. */
	typeId: number;

	/** True when the device is currently connected. */
	isConnected: boolean;

	/** Physical key/surface size metadata for the device. */
	size: {
		/** Number of rows on the device. */
		rows: number;

		/** Number of columns on the device. */
		columns: number;
	};
}

/**
 * Extended device information for the current script instance.
 * Includes the action instance position on the current device.
 */
interface CurrentDeviceInfo extends DeviceInfo {
	/** Zero-based row where the current action instance is loaded. Row 0 = bottom row. */
	row: number;

	/** Zero-based column where the current action instance is loaded. Column 0 = leftmost column. */
	column: number;
}

Run action details for midiScript

To enable result feedback from the run action, it is implemented in the functions library. Syntax:

RUN(path, waitForExit=true/false, OptionalParameters)

Example:
[(press){@l_procresultl_procresult:#RUN(“%trevligaspel%/myProc.cmd”, true)#}]

The @l_procresult variable in the example will be loaded with a combination of the content from StandardOutput and ErrorOutput (provided that you wait for the process exit).

Run action details for JavaScript

The run action is implemented in the io object. From the d.ts file:

	/**
	 * Runs a process silently.
	 * @param path Executable/script path
	 * @param waitForExit True = blocking, false = non-blocking
	 * @param parameters Optional parameters
	 */
	run(path: string, waitForExit?: boolean, parameters?: string): RunProcessResult;

interface RunProcessResult {
	Started: boolean;
	Completed: boolean;
	ExitCode: number;
	ProcessId: number;
	StandardOutput: string;
	StandardError: string;
}
1 Like

I haven’t tested nothing yet, but sometihing seems to be wrong in the installation.

After I run ‘se.trevligaspel.midi.4.3.0.5.drm.streamDeckPlugin‘, SD Preferences shows Midi plugin v4.3.0.1 and the bell is not showing any message that a new version of the Midi plugin has been installed.

Oh my. For some strange reason, the zip file shows version 4.2.0.4. I have no clue how that occurred. I’ll generate a new file now.

Here is the correct download. Sorry for the confusion.

Version 4.3.0.5 again

Hello,

I’m not sure i understand this one. If we have several devices connected, does it mean that we have one running JS script instance per device (and per button or dial)?.

I suppose row and column are relative to the buttons on the device. Correct ?

The “current device” refers to the device that has the button or dial with the script on it.

Correct.

In this version, the handling of the time event is fixed, and time references are verified for the text, textupper, title, and variable actions.

Version 4.3.0.9

I’m testing the run command in midiScript.

The following line is not doing nothing:

[(press){@l_processResult:#RUN("btt://trigger_named/?trigger_name=HUD Cubase Play", false)#}]

The following line is throwing an error:

[(press){RUN("btt://trigger_named/?trigger_name=HUD Cubase Play"}]

Is not possible to use RUN as a an action without additional parameters? Is not possible to run url commands?

What is “btt://” ?

It is an url generated by the app BetterTouchTool (BTT) to trigger an action configured inside that app.

MacOS main automation apps (like BetterTouchTool or Keyboard Maestro) have url options to trigger actions.

As example, this is a url from Keyboard Maestro:
kmtrigger://macro=61%29Witch

Please produce a log file when running those two commands.

done, this is the ID:
7cc018c2-ce5f-438d-9acb-cf03f8ba6869

In the current implementation, it searches for a file, so the “btt://” URL results in a “file not found” exception.

I have been testing run action in midiScript and js and it is working very well. It launches CLI and background apps as expected. I will post a summary of the tests when I finish them.

But I am facing a problem. This code is never changing the text of the button:

function OnGlobalVariableChanged(name, value) {
    switch (name) {
        case "cubaseNameSelectedTrack":
            ui.text("CHANGED")
            break;
    }
}

cubaseNameSelectedTrack is a global variable generated in a background midiscript.

What am I doing wrong?

Try to reference the variable name in all lowercase.

Indeed, it works with the variable in lowercase.

It would be possible that the js scripting engine switch variables to lowercase under the hood, like the midiScript engine?

Yes. Global variables are indeed global and accessible from both script engines, so the JavaScript engine uses the same variable controller as the midiScript engine has always used. MidiScript is case-insensitive, achieved through the variable controller, which stores variable names in lowercase.

Do you mean that if I save a global variable in midiScript it will always be converted to loewercase, so jsScript will only detect it in lowercase.

If I save a global var in jsScript it is not converted to lowercase?

Is it possible to use run like launch (in midiscript)?

I mean this:
[(press){launch:“Cubase_GoToLeftLocatorJ.app”}]
[(press){run:“Cubase_GoToLeftLocatorJ.app”}]

I say that both midiScript and JavaScript use the same variable controller to store global variables, and that this variable controller stores the names in lowercase.

When setting a global variable from JavaScript, all these commands hit the same variable:

gvar.MyVar
gvar.myvar
gvar["MYVAR"]
gvar.get("MyVar")
gvar.set("MYVAR", 123)

Unfortunately, I think the case of the name passed in the OnGlobalVariableChanged event may vary depending on the cause of the call. The safest approach is to force the name to lowercase before testing it.

I could change this to ensure lowercase is always passed in the event call, but that would be a breaking change, and I’m not sure it’s worth it. :thinking: