Skip to content

Accessing the xAPI via the Macro Editor** (section rxp-6)

Abstract

The Macro Editor is a Web Based IDE that's built into each Cisco Codec running ce9.2.X or higher (excluding the Sx10) that allows for the development of solutions using the Device xAPI and ES6 Javascript. In a sense, the Macro Editor is like a virtual room control processor built right into the product.

It's capable of running 10 active macros at any given time and allows for storage of up to 2mb of text across all files (Sounds small, but it's more than you think 😃).

You may have as many inactive macros as you can contain with the 2mb limit, which can be useful for storing information, organizing and modularizing work.

  • For example, some developers in the community have implemented function libraries formatted as a macro, such as
    • Gui-Do: A suite of functions that enables dynamic UI generation with the use of JSON Object
    • Audio Zone Manager: Or AZM is a suite of function that enables the mapping of audio microphones inputs to other resources for audio based automation in space.

Important

Note

This section is meant to teach your the structure of the xAPI when working the Macro Editor.

Though written in Javascript, this is not a javascript tutorial. There are links to relevant topics throughout the section in case you're stuck on any particular topic

Part 3: Building a Customization using Macros will leverage the Macro Editor and the UI Extensions of your codec to develop a solution using the xAPI

Syntax covered here is not only relevant for the Macro Editor but also the jsxapi Node.Js module which is not covered in this Lab

Section Requirements

Download the MacroPak below, these Macros will be used throughout this section

  • Click the icon below to Download the MacroPak


    MacroPak
    MacroPak

Enabling Macros (rxp-6.1)

  • Login to your Codec's Web UI
  • Navigate to Settings>Macro Editor
    • The Macro Editor is disabled by Default, press enable
Tip

Enabling through the WebUI as we had above can be don via the xAPI as well.

Running xConfiguration Macros Mode: On does the same thing.

You can even run xConfigurations in bulk across your portfolio using Webex Control Hub or Ce-Deploy, both are covered in, regards to Macro Customization, part 4 of this lab.

Vidcast: Macro Editor IDE Review

Vidcast: Installing the MacroPack

Executing xCommands (rxp-6.3)

Lesson: Execute an xCommand (rxp-6.3.1)

All device xAPIs are referenced by the imported xapi object. By default, a new Macro will contain

xAPI Import
import xapi from 'xapi';

Learn more about Imports

Unlike other ES6 Javascript environments, you only have access to base Javascript functions and techniques as well as the device's xAPI

  • You're NOT able to import external libraries into this environment.

All xAPI can be accessed by first referencing the xapi object following by the same command path using dot notation

Click on the tabs to see how Terminal Syntax relates to Macro Syntax

xCommand Time DateTime Get

OK
*r DateTimeGetResult (status=OK): 
*r DateTimeGetResult Day: 24
*r DateTimeGetResult Hour: 0
*r DateTimeGetResult Minute: 47
*r DateTimeGetResult Month: 9
*r DateTimeGetResult Second: 1
*r DateTimeGetResult Year: 2024
** end
import xapi from 'xapi';

xapi.Command.Time.DateTime.Get().then(time => console.log(time))

/* Log Output
{
  "Day": "24",
  "Hour": "0",
  "Minute": "47",
  "Month": "9",
  "Second": "44",
  "Year": "2024",
  "status": "OK"
}
*/
🤔 Why is .then(time => console.log(time)) trailing the command?

Well that's the nature of this environment. In a terminal session, the command is immediately followed by a response

But in working with the xAPI in a Macro or jsxapi NodeJs environment, the response is certainly there, but we need to capture in an object and then log it to the console.

Most, if not all, functions from the xapi object are Javascript Promises. When executed, they'll either resolve or reject (OK or Error) and you can handle them as you see fit in your automation.

Learn more about Promises

To get a bit more technical

In the Example above, we first call the xCommand Time DateTime Get command. JS Promises can leverage the .then() method, which allows us to take that value of a successful outcome and store it into another object, in this case time, and when time is populated with a value, we can immediately run a function => of this value to run additional processes. Here, we pass it into the in-built JS function; console.log, to log it into the Macro's log output.

If your function is rejected, then the .catch() method can handle those outcomes in the same way .then() works on resolutions.

Tip

Parameters for Macro Syntax are setup as a JSON Object and must be passed into a function as a parameter

Learn more about JSON

At a high level, functions defined in the xapi can have 1 or 2 function parameters pass. One being the parameters for the xAPI call writing in a JSON Object [Represented by myChildParams below], the other for multiline content (if available) [Represented by myMultiLineContent below]

It's important to note that not all xapi functions have multiline input, but it's good to know where it's placed should there be any

import xapi from 'xapi';

const myChildParams = { Parameter: 'One', Parameter: 2, Parameter: '...' };
const myMultiLineContent= `...`;

xapi.Parent.Child(myChildParams, myMultiLineContent);

Learn more about Functions

  • xAPI: xCommand Video Selfview Set

  • Task:

    • Activate the xCommands_Lesson-1_MacroPak_2-6-3 macro
    • Structure the xAPI Path above using Macro Syntax and apply the following parameters
      • Mode: On
      • FullScreenMode: On
      • OnMonitorRole: First
  • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

  • When Complete, deactivate the xCommands_Lesson-1_MacroPak_2-6-3 macro

View Successful Macro Syntax
import xapi from 'xapi';

xapi.Config.Video.Selfview.Set({ Mode: "On", FullScreenMode: "On", OnMonitorRole: "Off" });
import xapi from 'xapi';

xapi.Config.Video.Selfview.Set({ Mode: "On", FullScreenMode: "On", OnMonitorRole: "Off" }).then(resolution => {

  // Log the xAPI resolution
  console.log('Config.Video.Selfview.Set Resolution', resolution);

  /* Run Additional Function Here*/

}).catch(error => {

  // Log the xAPI rejection
  console.error('Config.Video.Selfview.Set Error', error);

  /* Run Additional Function Here*/

});

Learn more about Promises

import xapi from 'xapi';

const setSelfview = async function(parameters => {
  try {
    const runxAPI = await xapi.Config.Video.Selfview.Set(parameters);

    // Log the Resolution captured in a runxAPI object
    console.log(runxAPI);

    /* Run Additional Function Here*/

  } catch (error) (

    // Log the Rejection captured in a error object
    console.error(error);

    /* Run Additional Function Here*/

  );
});

// Run the setSelfview Function and pass in the Parameters for xCommand Video Selfview Set
setSelfview({ Mode: "On", FullScreenMode: "On", OnMonitorRole: "Off" });

Learn more about Async Functions

Lesson: Execute an xCommand with multiple arguments with the same name (rxp-6.3.2)

In cases where we need to declare multiple arguments of the same name, rather than duplicating and re-running the parameters, we instead leverage Javascript's Array capabilities

Learn more about Arrays

Click on the tabs to see how Terminal Syntax relates to Macro Syntax

xParent Child ChildParam_X: 1, ChildParam_X: 2

import xapi from 'xapi';

xapi.Parent.Child({
  ChildChildParam_X: [1, 2] // Rather than calling ChildParam_X twice, we'll simply place both values we need into an Array
})
  • xAPI(s):

    • xCommand Video Selfview Set
    • xCommand Video Input SetMainVideoSource
  • Task:

    • Activate the xCommands_Lesson-2_MacroPak_2-6-3 macro
    • Structure xCommand Video Input SetMainVideoSource using Macro Syntax and apply the following parameters, but assign the value 1 to ConnectorId twice
    • ConnectorId: 1
    • Layout: Equal
    • Add this xCommand to the showAndCompose() function
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xCommands_Lesson-2_MacroPak_2-6-3 macro

View Successful Macro Syntax
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#263-executing-xcommands
 * 
 * Lesson 2: Execute an xCommand with multiple arguments with the same name
 */

const showAndComposeCamera = function () {
  xapi.Command.Video.Selfview.Set({ Mode: 'On', FullscreenMode: 'On', OnMonitorRole: 'First' });

  // Enter your solution below this line
  xapi.Command.Video.Input.SetMainVideoSource({
    ConnectorId: [1, 1],
    Layout: 'Equal'
  })
  // Don't go past this line
}

showAndComposeCamera();

<!-- ??? challenge "Challenge: Log and Handle Errors"

- Convert the `showAndComposeCamera()` function into an Async Function
- Wrap all xAPI references in a Try Catch block
- Add a console log for a Successful outcome
- Add a console log for an Error

- Save the Macro and observe the log

<a class="md-button md-button--primary" href="../challengeAnswers/" target="_blank" >
  Giving Up? Check out the Challenge Answers Page <i class="fa-solid fa-square-up-right"></i>
</a> -->
Lesson: Execute an xCommand with a multiline argument (rxp-6.3.3)

Click on the tabs to see how Terminal Syntax relates to Macro Syntax

[Command Path]
[Multi Line Content]
.

import xapi from 'xapi';

const myChildParams = { Parameter: 'One', Parameter: 2, Parameter: '...' };
const myMultiLineContent= `...`;

xapi.Parent.Child(myChildParams, myMultiLineContent);
  • xAPI(s):

    • xCommand Video Selfview Set
    • xCommand Video Input SetMainVideoSource
    • xCommand UserInterface Extensions Panel Save
  • Task:

    • Activate the xCommands_Lesson-3_MacroPak_2-6-3 macro
    • Assign the value wx1_lab_multilineCommand to the myPanelId object
    • Assign the following XML payload to the myUserinterface object
      <Extensions>
        <Panel>
          <Order>1</Order>
          <PanelId>wx1_lab_multilineCommand</PanelId>
          <Location>HomeScreen</Location>
          <Icon>Info</Icon>
          <Color>#00FFFF</Color>
          <Name>MultiLine Command [2.6.3]</Name>
          <ActivityType>Custom</ActivityType>
        </Panel>
      </Extensions>
      
    • Structure xCommand UserInterface Extensions Panel Save using Macro Syntax and apply the following parameters
      • PanelId [Use the myPanelId object for this field]
      • body [Use the myUserinterfaceXML object for this field] (This is a MultiLine Argument)
    • Add this xCommand to the buildUserInterface() function
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xCommands_Lesson-3_MacroPak_2-6-3 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#263-executing-xcommands
 * 
 * Lesson 3: Execute an xCommand with a multiline argument
 */

// Assign values to these Objects
const myPanelId = 'wx1_lab_multilineCommand';

const myUserinterfaceXML = `<Extensions>
      <Panel>
        <Order>1</Order>
        <PanelId>wx1_lab_multilineCommand</PanelId>
        <Location>HomeScreen</Location>
        <Icon>Info</Icon>
        <Color>#00FFFF</Color>
        <Name>MultiLine Command [2.6.3]</Name>
        <ActivityType>Custom</ActivityType>
      </Panel>
    </Extensions>`


const buildUserInterface = async function (){
  try {
    // Enter your solution below this line

    const saveUI = await xapi.Command.UserInterface.Extensions.Panel.Save({ PanelId: myPanelId }, myUserinterfaceXML)

    // Don't go past this line
    console.log(`Panel [${myPanelId}] saved to the codec`)
  } catch (e){
    console.error(e)
  }
}


async function cleanupLesson2(){
  await xapi.Command.Video.Selfview.Set({Mode: 'Off'});
  await xapi.Command.Video.Input.SetMainVideoSource({ConnectorId: 1, Layout: 'Equal'});
}

async function init(){
  await cleanupLesson2()

  await buildUserInterface();
}

init();
🤔 Having issues with saving Strings to Objects in your macro?

There are 3 ways to define string literals

Key Name Example Extra Properties
' Single Quote const myString = "It's a sunny day." Can encapsulate a string with single quotes ' inside
" Double Quote const myOtherString = 'They said, "Hello!"'; Can encapsulate a string with double quotes " inside
` Backtick Quote const myFinalString = They didn't say "World" Can encapsulate double and single quotes, allows for multiline strings, allows for string interpolation

Learn more about Strings

Lesson: Execute an xCommand which generates data and responds (rxp-6.3.4)

When collecting data from an xCommand in the Macro Editor, you either need to use the .then() method and log that value to the console or use an Async function to capture the value of that xCommand into a object, then log that object

  • xAPI: xCommand UserInterface Extensions List

  • Task:

    • Activate the xCommands_Lesson-4_MacroPak_2-6-3 macro
    • Structure xCommand UserInterface Extensions List using Macro Syntax and do 1 of the following

      • Use .then() to capture the value of xCommand UserInterface Extensions List then log that value to the console

        Or

      • declare an async function called checkExtensions, place xCommand UserInterface Extensions List written in Macro Syntax

        • Wrap that in a Try Catch statement
        • Assign the value of the xAPI to an object
        • Then log the value of that object to the console
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xCommands_Lesson-4_MacroPak_2-6-3 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#263-executing-xcommands
 * 
 * Lesson 4: Execute an xCommand which generates data and responds
 */

xapi.Command.UserInterface.Extensions.List().then(ext => {
  console.log(ext);
}).catch(error => {
  console.error(error);
});
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#263-executing-xcommands
 * 
 * Lesson 4: Execute an xCommand which generates data and responds
 */

const checkExtensions = async function () {

  try {
    const getExtensions = await xapi.Command.UserInterface.Extensions.List();
    console.log(getExtensions);
  } catch (error) {
    console.error(error);
  };
};

checkExtensions();

Setting, Getting and Subscribing to xConfigurations (rxp-6.4)

Abstract

Getting xConfiguration values, and later on xStatus Values, use the nearly same techniques for xCommands that generate data and respond.

However, when Getting an xConfiguration or an xStatus, you'll need to add the .get() method at the end of the xAPI call.

Subsequently, when Setting an xConfiguration, you'll need to add the .set() method at the end of the xAPI call.

Compare Macro Command vs Config syntax

xapi.Command.ChildPath (childParameter, childMultiLine)

xapi.Config.ChildPath .get()

xapi.Config.ChildPath .get('ChildValue')

We'll continue the remainder of the examples with only Async Await syntax, as a best practice, but if you're familiar with .then(), .catch() and .finally() syntax and prefer writing like that, feel free to do so

Lesson: Get an xConfiguration Value (rxp-6.4.1)
  • xAPI: xConfig Audio DefaultVolume

  • Task:

    • Activate the xConfigs_Lesson-1_MacroPak_2-6-4 macro
    • Modify the getConfigValue() function by replacing the existing value of targetConfig with xConfig Audio DefaultVolume written in Macro Syntax
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xConfigs_Lesson-1_MacroPak_2-6-4 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#264-setting-getting-and-subscribing-to-xconfigurations
 * 
 * Lesson 1: Getting an xConfiguration Value
 */

// Enter your solution below this line


const getConfigValue = async function () {
  try {
    // Modify targetConfig below

    const targetConfig = await xapi.Config.Audio.DefaultVolume.get();

    // Don't go past this line
    console.log('DefaultVolume:', targetConfig)
  } catch (e) {
    console.error(e);
  };
};

getConfigValue();
Timestamp Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xConfigs_Lesson-1_MacroPak_2-6-4 QJS Ready
HH:MM:SS xConfigs_Lesson-1_MacroPak_2-6-4 DefaultVolume: 75
Lesson: Set a new xConfiguration Value (rxp-6.4.2)
  • xAPI: xConfig Audio DefaultVolume

  • Task:

    • Activate the xConfigs_Lesson-2_MacroPak_2-6-4 macro
    • Modify the setConfigValue() function by replacing the existing value of targetConfig with xConfig Audio DefaultVolume written in Macro Syntax
    • Instead of hardcoding the value we want to set, place the value parameter into the .set(value) method instead
      • This will allow us to change this value easier as we call the function in different parts of our script
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xConfigs_Lesson-2_MacroPak_2-6-4 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#264-setting-getting-and-subscribing-to-xconfigurations
 * 
 * Lesson 2: Set a new xConfiguration Value
 */

// Enter your solution below this line


const setConfigValue = async function (value = 50) {
  try {
    // Modify targetConfig below

    const targetConfig = await xapi.Config.Audio.DefaultVolume.set(value);

    // Don't go past this line
    console.debug('DefaultVolume Set');
  } catch (e) {
    console.error(e);
  };
};


const getConfigValue = async function () {
  try {
    const targetConfig = await xapi.Config.Audio.DefaultVolume.get();
    console.log('DefaultVolume:', targetConfig);
  } catch (e) {
    console.error(e);
  };
};

async function init(){

  await setConfigValue(100); // <-- Change this Value [0-100] and Resave

  await getConfigValue();
}

init();
Timestamp Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xConfigs_Lesson-2_MacroPak_2-6-4 QJS Ready
HH:MM:SS xConfigs_Lesson-2_MacroPak_2-6-4 DefaultVolume: [Some Value]
Lesson: Get multiple xConfigurations under a Common Node (rxp-6.4.3)
  • xAPI: xConfig Audio

  • Task:

    • Activate the xConfigs_Lesson-3_MacroPak_2-6-4 macro
    • Modify the getConfigValue() function by replacing the existing value of targetConfig with xConfig Audio written in Macro Syntax
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xConfigs_Lesson-3_MacroPak_2-6-4 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#264-setting-getting-and-subscribing-to-xconfigurations
 * 
 * Lesson 1: Getting an xConfiguration Value
 */

// Enter your solution below this line

const getConfigValue = async function () {
  try {
    // Modify targetConfig below

    const targetConfig = await xapi.Config.Audio.get();

    // Don't go past this line
    console.log('DefaultVolume:', targetConfig)
  } catch (e) {
    console.error(e);
  };
};

getConfigValue();
Timestamp Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xConfigs_Lesson-3_MacroPak_2-6-4 QJS Ready
HH:MM:SS xConfigs_Lesson-3_MacroPak_2-6-4 {"DefaultVolume":"100","Ethernet":{"Encryption":"Required","SAPDiscovery":{"Address":"239.255.255.255","Mode":"Off"}},"Input":{"Ethernet":[{"Channel":[{"Gain":"45","Mode":"On","Pan":"Mono","Zone":"1","id":"1"},{"Gain":"45","Mode":"On","Pan":"Mono","Zone":"1","id":"2"},{"Gain":"45","Mode":"On","Pan":"Mono","Zone":"1","id":"3"},{"Gain":"45","Mode":"On","Pan":"Mono","Zone":"1","id":"4"},{"Gain":"45","Mode":"On","Pan":"Mono","Zone":"1","id":"5"},{"Gain":"45","Mode":"On","Pan":"Mono","Zone":"1","id":"6"},{"... And the list goes on"}],"EchoControl":{"Mode":"On","NoiseReduction":"On"},"Equalizer":{"ID":"1","Mode":"Off"},"Mode":"On","id":"1"}]}}{..."And the List Goes On"}
Lesson: Subscribe and Unsubscribe to an xConfiguration (rxp-6.4.4)

Info

Subscriptions in the Macro Editor introduce another method we can append to the end of the path called .on()

.on() allows us to subscribe to any changes in xConfigurations, xStatuses and xEvents until the script has either stopped or until the xAPI path is unsubscribed too

.on() expect an object, similar to using .then() for you to place the incoming data and run function off of it

Click on the tabs to see how Terminal Syntax relates to Macro Syntax

xFeedback Register Configuration/Child/Child
** end

OK
*c xConfiguration Child Child Value: 85
** end
*c xConfiguration Child Child Value: 44
** end
*c xConfiguration Child Child Value: 36
** end
import xapi from 'xapi';

xapi.Configuration.Child.Child.on(ChildValue => {
  console.log('New ChildValue:', ChildValue);
});

/* Log Output
New ChildValue: 85
New ChildValue: 44
New ChildValue: 36
*/
  • xAPI: xConfiguration Audio DefaultVolume

  • Task:

    • Activate the xConfigs_Lesson-4_MacroPak_2-6-4 macro
    • Modify the subscribeToDefaultVolume object by replacing it's value with xConfig Audio written in Macro Syntax using the .on() method
      • In order to unsubscribe, we need to assign our xAPI subscription to an object, so we can later call it, which will end it's subscription
      • For example, after you assign the subscribeToDefaultVolume properly, running subscribeToDefaultVolume() will stop your active subscription
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

      • NOTE: This macro will automatically unsubscribe for you. Review those steps, to get a better understand as to how we unsubscribe.
    • When Complete, deactivate the xConfigs_Lesson-4_MacroPak_2-6-4 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#264-setting-getting-and-subscribing-to-xconfigurations
 * 
 * Lesson 4: Subscribe and Unsubscribe to an xConfiguration
*/

const delay_in_seconds = 10;

// Edit this Object to include your xConfiguration Subscription
const subscribeToDefaultVolume = xapi.Config.Audio.DefaultVolume.on(event => {
  console.log('DefaultVolume Set to:', event)
})

// Do not edit past this line, but feel free to review what's going on :)

// Here, we use JS Timeouts to set an action to run after X seconds. Timeouts use milliseconds, hence why we multiply by 1000
setTimeout(() => {

  subscribeToDefaultVolume(); //<-- By calling the Object we assigned our Subscription too as a function(), we will unsubscribe from it

  console.warn("DefaultVolume Subscription stopped!");

}, delay_in_seconds * 1000)


// Here, we're randomly assigning a value between 1 and 100 to the Default Volume, so we can see that configuration on our Subscription
function setRandomDefaultVolume() {
  const randomValue = Math.floor(Math.random() * 100) + 1;

  xapi.Config.Audio.DefaultVolume.set(randomValue);
}


// This countdown is used to help you visualize when the process will complete it's course
// We use console.warn to have this countdown print in another color in the Macro Console
function countdown(startNumber) {
  let currentNumber = startNumber;

  console.warn(`DefaultVolume Subscription stopping in [${currentNumber}] seconds`);

  const interval = setInterval(() => {
    currentNumber--;
    if (currentNumber > 0) {
      console.warn(`DefaultVolume Subscription stopping in [${currentNumber}] seconds`);
    }

    if (currentNumber < 1) {
      clearInterval(interval);
    }
  }, 1000);
}

function init() {
  setInterval(() => {
    setRandomDefaultVolume();
  }, 500)

  countdown(delay_in_seconds);
}

init();
Time Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Subscription stopping in [5] seconds
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 QJS Ready
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 70
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Subscription stopping in [4] seconds
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 48
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 13
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Subscription stopping in [3] seconds
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 92
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 52
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Subscription stopping in [2] seconds
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 46
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 69
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Subscription stopping in [1] seconds
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 21
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Set to: 57
HH:MM:SS xConfigs_Lesson-4_MacroPak_2-6-4 DefaultVolume Subscription stopped!
Lesson: Subscribe and Unsubscribe to Multiple xConfigurations under a Common Node (rxp-6.4.5)

Info

Just like we can subscribe to 1 point of interest in an xConfig branch, we can subscribe to a Higher Common Node as well

We'll do so for the Airplay Config section of you codec

  • xAPI: xConfiguration Video Input Airplay

  • Task:

    • Activate the xConfigs_Lesson-5_MacroPak_2-6-4 macro
    • Modify the subscribeToAirplay object by replacing it's value with xConfiguration Video Input Airplay written in Macro Syntax using the .on() method
      • In order to unsubscribe, we need to assign our xAPI subscription to an object, so we can later call it, which will end it's subscription
      • For example, after you assign the subscribeToAirplay properly, running subscribeToAirplay() will stop your active subscription
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

      • NOTE: This macro will automatically unsubscribe for you. Review those steps, to get a better understand as to how we unsubscribe.
    • When Complete, deactivate the xConfigs_Lesson-5_MacroPak_2-6-4 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#264-setting-getting-and-subscribing-to-xconfigurations
 * 
 * Lesson 5: Subscribe and Unsubscribe to Multiple xConfigurations under a Common Node
 */

const delay_in_seconds = 5;

// Edit this Object to include your xConfiguration Subscription
const subscribeToAirplay = xapi.Config.Video.Input.Airplay.on(event => {
  console.log('Airplay Changes:', event)
})

// Do not edit past this line, but feel free to review what's going on :)

// Here, we use JS Timeouts to set an action to run after X seconds. Timeouts use milliseconds, hence why we multiply by 1000
setTimeout(() => {

  subscribeToAirplay(); //<-- By calling the Object we assigned our Subscription too as a function(), we will unsubscribe from it

  console.warn("Airplay Subscription stopped!");

}, delay_in_seconds * 1000)


// Here, we're randomly assigning a values to the Airplay config, so we can see that configuration on our Subscription
function setRandomAirplayConfigs() {

  function randomNumber() {
    return Math.floor(Math.random() * 10);
  }

  const randomPass = `${randomNumber()}${randomNumber()}${randomNumber()}${randomNumber()}`

  xapi.Config.Video.Input.AirPlay.Mode.set(Math.random() < 0.5 ? "On" : "Off");

  xapi.Config.Video.Input.AirPlay.Beacon.set(Math.random() < 0.5 ? "Auto" : "Off");

  xapi.Config.Video.Input.AirPlay.Password.set(randomPass);
}


// This countdown is used to help you visualize when the process will complete it's course
// We use console.warn to have this countdown print in another color in the Macro Console
function countdown(startNumber) {
  let currentNumber = startNumber;

  console.warn(`Airplay Subscription stopping in [${currentNumber}] seconds`);

  const interval = setInterval(() => {
    currentNumber--;
    if (currentNumber > 0) {
      console.warn(`Airplay Subscription stopping in [${currentNumber}] seconds`);
    }

    if (currentNumber < 1) {
      clearInterval(interval);
    }
  }, 1000);
}

function init() {
  setInterval(() => {
    setRandomAirplayConfigs();
  }, 500)

  countdown(delay_in_seconds);
}

init();
Time Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Subscription stopping in [5] seconds
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 QJS Ready
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Mode":"On"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Beacon":"Off"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Subscription stopping in [4] seconds
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Mode":"Off"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Mode":"On"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Subscription stopping in [3] seconds
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Beacon":"Auto"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Subscription stopping in [2] seconds
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Mode":"Off"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Beacon":"Off"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Beacon":"Auto"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Subscription stopping in [1] seconds
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Mode":"On"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Mode":"Off"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Changes: {"Password":"***"}
HH:MM:SS xConfigs_Lesson-5_MacroPak_2-6-4 Airplay Subscription stopped!

Getting and Subscribing to xStatuses (rxp-6.5)

Lesson: Get an xStatus Value (rxp-6.5.1)
  • xAPI: xStatus Audio Volume

  • Task:

    • Activate the xStatuses_Lesson-1_MacroPak_2-6-4 macro
    • Modify the getStatusValue() function by replacing the existing value of targetStatus with xStatus Audio Volume written in Macro Syntax
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xStatuses_Lesson-1_MacroPak_2-6-4 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#265-getting-and-subscribing-to-xstatuses
 * 
 * Lesson 1: Getting an xStatus Value
 */

// Enter your solution below this line

const getStatusValue = async function () {
  try {
    // Modify targetStatus below

    const targetStatus = await xapi.Status.Audio.Volume.get();

    // Don't go past this line
    console.log('Volume:', targetStatus)
  } catch (e) {
    console.error(e);
  };
};

getStatusValue();
Timestamp Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xStatuses_Lesson-1_MacroPak_2-6-4 QJS Ready
HH:MM:SS xStatuses_Lesson-1_MacroPak_2-6-4 Volume: 50
Lesson: Get multiple xStatuses under a Common Node (rxp-6.5.2)
  • xAPI: xStatus Audio

  • Task:

    • Activate the xStatuses_Lesson-2_MacroPak_2-6-4 macro
    • Modify the getStatusValue() function by replacing the existing value of targetStatus with xStatus Audio written in Macro Syntax
    • Save your Macro and monitor the Macro Console as well as the Device to see if you had a successful response

    • When Complete, deactivate the xStatuses_Lesson-2_MacroPak_2-6-4 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#265-getting-and-subscribing-to-xstatuses
 * 
 * Lesson 2: Getting an xStatus Value
 */

// Enter your solution below this line

const getStatusValue = async function () {
  try {
    // Modify targetStatus below

    const targetStatus = await xapi.Status.Audio.get();

    // Don't go past this line
    console.log(targetStatus)
  } catch (e) {
    console.error(e);
  };
};

getStatusValue();
Timestamp Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xStatuses_Lesson-2_MacroPak_2-6-4 QJS Ready
HH:MM:SS xStatuses_Lesson-2_MacroPak_2-6-4 { "Devices": { "Bluetooth": { "ActiveProfile": "None" }, "HandsetUSB": { "ConnectionStatus": "NotConnected", "Cradle": "OnHook" }, "HeadsetUSB": { "ConnectionStatus": "NotConnected", "Description": "", "Manufacturer": "" } }, "Input": { "Connectors": { "HDMI": [ { "Mute": "On", "id": "1" } ], "Microphone": [ { "ConnectionStatus": "Connected", "id": "1" }, { "ConnectionStatus": "NotConnected", "id": "2" }, { "ConnectionStatus": "NotConnected", "id": "3" } ], "USBC": [ { "Mute": "On", "id": "1" } ] } } }{..."And the List Goes On"}
Lesson: Subscribe and Unsubscribe to an xStatus (rxp-6.5.3)
  • xAPI: xStatus Audio Volume

  • Task:

    • Activate the xStatuses_Lesson-3_MacroPak_2-6-5 macro
    • Modify the subscribeToVolume object by replacing it's value with xStatus Audio Volume written in Macro Syntax using the .on() method
      • In order to unsubscribe, we need to assign our xAPI subscription to an object, so we can later call it, which will end it's subscription
      • For example, after you assign the subscribeToVolume properly, running subscribeToVolume() will stop your active subscription
    • Save your Macro, raise and lower the volume on your Codec and monitor the Macro Console to see if you had a successful response

      • NOTE: This macro will automatically unsubscribe for you. Review those steps, to get a better understand as to how we unsubscribe.
    • When Complete, deactivate the xStatuses_Lesson-3_MacroPak_2-6-5 macro

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#265-getting-and-subscribing-to-xstatuses
 * 
 * Lesson 3: Subscribe and Unsubscribe to an xStatus
*/

const delay_in_seconds = 10;

// Edit this Object to include your xStatus Subscription
const subscribeToVolume = xapi.Status.Audio.Volume.on(vol => {
  console.log('Volume:', vol)
});

// Do not edit past this line, but feel free to review what's going on :)

// Here, we use JS Timeouts to set an action to run after X seconds. Timeouts use milliseconds, hence why we multiply by 1000
setTimeout(() => {

  subscribeToVolume(); //<-- By calling the Object we assigned our Subscription too as a function(), we will unsubscribe from it

  console.warn("Volume Subscription stopped!");

}, delay_in_seconds * 1000)


// This countdown is used to help you visualize when the process will complete it's course
// We use console.warn to have this countdown print in another color in the Macro Console
function countdown(startNumber) {
  let currentNumber = startNumber;

  console.warn(`Volume Subscription stopping in [${currentNumber}] seconds`);

  const interval = setInterval(() => {
    currentNumber--;
    if (currentNumber > 0) {
      console.warn(`Volume Subscription stopping in [${currentNumber}] seconds`);
    }

    if (currentNumber < 1) {
      clearInterval(interval);
    }
  }, 1000);
}

function init() {
  countdown(delay_in_seconds);
}

init();
Time Source Message
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [10] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 QJS Ready
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [9] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [8] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [7] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 80
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 85
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [6] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 90
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [5] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 85
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [4] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 80
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [3] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 75
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 70
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [2] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 65
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopping in [1] seconds
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume: 60
HH:MM:SS xStatuses_Lesson-3_MacroPak_2-6-5 Volume Subscription stopped!
Lesson: Subscribe and Unsubscribe to Multiple xStatuses under a Common Node (rxp-6.5.4)
  • xAPI: xStatus Cameras Camera[N] Position

  • Task:

    • Activate the xStatuses_Lesson-4_MacroPak_2-6-5 macro
    • Modify the subscribeToCameraPositions object by replacing it's value with xStatus Cameras Camera[N] Position written in Macro Syntax using the .on() method
      • In order to unsubscribe, we need to assign our xAPI subscription to an object, so we can later call it, which will end it's subscription
      • For example, after you assign the subscribeToCameraPositions properly, running subscribeToCameraPositions() will stop your active subscription
      • Access the Codec's Control Panel on it's touch interface
      • Select Cameras
      • Select Manual
      • Then use the Control Wheel, Zoom In (+) and and Zoom out (-) buttons and observe your Macro Log output

      Save your Macro, and perform the following steps

      • NOTE: This macro will automatically unsubscribe for you. Review those steps, to get a better understand as to how we unsubscribe.
    • When Complete, deactivate the xStatuses_Lesson-4_MacroPak_2-6-5 macro

Accessing the Camera Menu

Navigate to Camera Control Menu GIF

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#265-getting-and-subscribing-to-xstatuses
 * 
 * Lesson 4: Subscribe and Unsubscribe to Multiple xStatuses under a Common Node
*/

const delay_in_seconds = 10;

// Edit this Object to include your xStatus Subscription
const subscribeToCameraPositions = xapi.Status.Cameras.Camera.Position.on(event => {
  console.log(event)
});

// Do not edit past this line, but feel free to review what's going on :)

// Here, we use JS Timeouts to set an action to run after X seconds. Timeouts use milliseconds, hence why we multiply by 1000
setTimeout(() => {

  subscribeToCameraPositions(); //<-- By calling the Object we assigned our Subscription too as a function(), we will unsubscribe from it

  console.warn("CameraPositions Subscription stopped!");

}, delay_in_seconds * 1000)


// This countdown is used to help you visualize when the process will complete it's course
// We use console.warn to have this countdown print in another color in the Macro Console
function countdown(startNumber) {
  let currentNumber = startNumber;

  console.warn(`CameraPositions Subscription stopping in [${currentNumber}] seconds`);

  const interval = setInterval(() => {
    currentNumber--;
    if (currentNumber > 0) {
      console.warn(`CameraPositions Subscription stopping in [${currentNumber}] seconds`);
    }

    if (currentNumber < 1) {
      clearInterval(interval);
    }
  }, 1000);
}

function init() {
  countdown(delay_in_seconds);
}

init();
Time Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [10] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 QJS Ready
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Zoom":"4295"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [9] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Zoom":"5662"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [8] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Pan":"-65"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [7] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Pan":"-64","Tilt":"123"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [6] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Pan":"-61","Tilt":"-20"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Pan":"-24","Tilt":"-19"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [5] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Tilt":"47"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [4] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Zoom":"4384"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [3] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Tilt":"-14"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [2] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 {"Pan":"14"}
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopping in [1] seconds
HH:MM:SS xStatuses_Lesson-4_MacroPak_2-6-5 CameraPositions Subscription stopped!

Subscribing to xEvents (rxp-6.6)

Lesson: Subscribe and Unsubscribe to an xEvent (rxp-6.6.1)
  • xAPI: xEvent UserInterface Extensions Widget Action

  • Task:

    • Activate the xEvents_Lesson-1_MacroPak_2-6-6 macro
    • Modify the subscribeToWidgetActions object by replacing it's value with xEvent UserInterface Widget Action written in Macro Syntax using the .on() method
      • In order to unsubscribe, we need to assign our xAPI subscription to an object, so we can later call it, which will end it's subscription
      • For example, after you assign the subscribeToWidgetActions properly, running subscribeToWidgetActions() will stop your active subscription
    • Save your Macro, open the MultiLine Command [2.6.6] Panel on your Codec's touch interface, press one or more of the buttons and observe the Macro Log Output

      • NOTE: This macro will automatically unsubscribe for you. Review those steps, to get a better understand as to how we unsubscribe.
    • When Complete, deactivate the xEvents_Lesson-1_MacroPak_2-6-6 macro

Open the MultiLine Command [2.6.6] Panel

Open the MultiLine Command [2.6.6] Panel

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#266-subscribing-to-xevents
 * 
 * Lesson 1: Subscribe and Unsubscribe to an xEvent
*/

const delay_in_seconds = 10;

// Edit this Object to include your xEvent Subscription
const subscribeToWidgetActions = xapi.Event.UserInterface.Extensions.Widget.Action.on(event => {
  console.log(event)
});

// Do not edit past this line, but feel free to review what's going on :)

// Here, we use JS Timeouts to set an action to run after X seconds. Timeouts use milliseconds, hence why we multiply by 1000
setTimeout(() => {

  subscribeToWidgetActions(); //<-- By calling the Object we assigned our Subscription too as a function(), we will unsubscribe from it

  console.warn("WidgetActions Subscription stopped!");

}, delay_in_seconds * 1000)


// This countdown is used to help you visualize when the process will complete it's course
// We use console.warn to have this countdown print in another color in the Macro Console
function countdown(startNumber) {
  let currentNumber = startNumber;

  console.warn(`WidgetActions Subscription stopping in [${currentNumber}] seconds`);

  const interval = setInterval(() => {
    currentNumber--;
    if (currentNumber > 0) {
      console.warn(`WidgetActions Subscription stopping in [${currentNumber}] seconds`);
    }

    if (currentNumber < 1) {
      clearInterval(interval);
    }
  }, 1000);
}

const myPanelId = 'wx1_lab_multilineCommand';

const myUserinterfaceXML = `<Extensions>
  <Panel>
    <Order>1</Order>
    <PanelId>wx1_lab_multilineCommand</PanelId>
    <Location>HomeScreen</Location>
    <Icon>Info</Icon>
    <Color>#FC5143</Color>
    <Name>MultiLine Command [2.6.6]</Name>
    <ActivityType>Custom</ActivityType>
    <Page>
      <Name>Page</Name>
      <Row>
        <Name>Buttons</Name>
        <Widget>
          <WidgetId>wx1_GroupButton</WidgetId>
          <Type>GroupButton</Type>
          <Options>size=4</Options>
          <ValueSpace>
            <Value>
              <Key>GroupButton_A</Key>
              <Name>A</Name>
            </Value>
            <Value>
              <Key>GroupButton_B</Key>
              <Name>B</Name>
            </Value>
            <Value>
              <Key>GroupButton_C</Key>
              <Name>C</Name>
            </Value>
          </ValueSpace>
        </Widget>
        <Widget>
          <WidgetId>wx1_TextButton</WidgetId>
          <Name>Text</Name>
          <Type>Button</Type>
          <Options>size=1</Options>
        </Widget>
        <Widget>
          <WidgetId>wx1_IconButton</WidgetId>
          <Type>Button</Type>
          <Options>size=1;icon=green</Options>
        </Widget>
        <Widget>
          <WidgetId>wx1_SpinnerButton</WidgetId>
          <Type>Spinner</Type>
          <Options>size=2</Options>
        </Widget>
      </Row>
      <Row>
        <Name>Control Wheel</Name>
        <Widget>
          <WidgetId>wx1_ControlWheel</WidgetId>
          <Type>DirectionalPad</Type>
          <Options>size=4</Options>
        </Widget>
      </Row>
      <Row>
        <Name>Toggle and Slider</Name>
        <Widget>
          <WidgetId>wx1_Toggle</WidgetId>
          <Type>ToggleButton</Type>
          <Options>size=1</Options>
        </Widget>
        <Widget>
          <WidgetId>wx1_Slider</WidgetId>
          <Type>Slider</Type>
          <Options>size=3</Options>
        </Widget>
      </Row>
      <Options/>
    </Page>
  </Panel>
</Extensions>`


const buildUserInterface = async function () {
  try {
    const saveUI = await xapi.Command.UserInterface.Extensions.Panel.Save({ PanelId: myPanelId }, myUserinterfaceXML)
    console.log(`Panel [${myPanelId}] saved to the codec`)
  } catch (e) {
    console.error(e)
  }
}

function init() {
  countdown(delay_in_seconds);

  buildUserInterface()
}

init();
Time Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 QJS Ready
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [10] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"GroupButton_A","WidgetId":"wx1_GroupButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"GroupButton_A","WidgetId":"wx1_GroupButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"GroupButton_B","WidgetId":"wx1_GroupButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [9] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 Panel [wx1_lab_multilineCommand] saved to the codec
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"GroupButton_B","WidgetId":"wx1_GroupButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"GroupButton_C","WidgetId":"wx1_GroupButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"GroupButton_C","WidgetId":"wx1_GroupButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [8] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"","WidgetId":"wx1_TextButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"","WidgetId":"wx1_TextButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"clicked","Value":"","WidgetId":"wx1_TextButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"","WidgetId":"wx1_IconButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [7] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"","WidgetId":"wx1_IconButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"clicked","Value":"","WidgetId":"wx1_IconButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"decrement","WidgetId":"wx1_SpinnerButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [6] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"decrement","WidgetId":"wx1_SpinnerButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"clicked","Value":"decrement","WidgetId":"wx1_SpinnerButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [5] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"increment","WidgetId":"wx1_SpinnerButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"increment","WidgetId":"wx1_SpinnerButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"clicked","Value":"increment","WidgetId":"wx1_SpinnerButton","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [4] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"up","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"up","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"clicked","Value":"up","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"left","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [3] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"left","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"clicked","Value":"left","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [2] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"pressed","Value":"center","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"released","Value":"center","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"clicked","Value":"center","WidgetId":"wx1_ControlWheel","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopping in [1] seconds
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 {"Type":"changed","Value":"off","WidgetId":"wx1_Toggle","id":"1"}
HH:MM:SS xEvents_Lesson-1_MacroPak_2-6-6 WidgetActions Subscription stopped!
Lesson: Subscribe and Unsubscribe to Multiple xEvents under a Common Node (rxp-6.6.2)
  • xAPI: xEvent UserInterface Extensions

  • Task:

    • Activate the xEvents_Lesson-1_MacroPak_2-6-6 macro
    • Modify the subscribeToAllExtensions object by replacing it's value with xEvent UserInterface written in Macro Syntax using the .on() method
      • In order to unsubscribe, we need to assign our xAPI subscription to an object, so we can later call it, which will end it's subscription
      • For example, after you assign the subscribeToAllExtensions properly, running subscribeToAllExtensions() will stop your active subscription
    • Save your Macro, open the MultiLine Command [2.6.6] Panel on your Codec's touch interface, press one or more of the buttons and observe the Macro Log Output

      • NOTE: This macro will automatically unsubscribe for you. Review those steps, to get a better understand as to how we unsubscribe.
    • When Complete, deactivate the xEvents_Lesson-1_MacroPak_2-6-6 macro

Open the MultiLine Command [2.6.6] Panel

Open the MultiLine Command [2.6.6] Panel

View Successful Macro Syntax and Log output
import xapi from 'xapi';

/**
 * Lab Guide: https://webexcc-sa.github.io/LAB-1451/wx1_1451_part_2/#266-subscribing-to-xevents
 * 
 * Lesson 2: Subscribe and Unsubscribe to Multiple xEvents under a Common Node
*/

const delay_in_seconds = 10;

// Edit this Object to include your xEvent Subscription
const subscribeToAllExtensions = xapi.Event.UserInterface.Extensions.on(event => {
  console.log(event)
});

// Do not edit past this line, but feel free to review what's going on :)

// Here, we use JS Timeouts to set an action to run after X seconds. Timeouts use milliseconds, hence why we multiply by 1000
setTimeout(() => {

  subscribeToAllExtensions(); //<-- By calling the Object we assigned our Subscription too as a function(), we will unsubscribe from it

  console.warn("AllExtensions Subscription stopped!");

}, delay_in_seconds * 1000)


// This countdown is used to help you visualize when the process will complete it's course
// We use console.warn to have this countdown print in another color in the Macro Console
function countdown(startNumber) {
  let currentNumber = startNumber;

  console.warn(`AllExtensions Subscription stopping in [${currentNumber}] seconds`);

  const interval = setInterval(() => {
    currentNumber--;
    if (currentNumber > 0) {
      console.warn(`AllExtensions Subscription stopping in [${currentNumber}] seconds`);
    }

    if (currentNumber < 1) {
      clearInterval(interval);
    }
  }, 1000);
}

const myPanelId = 'wx1_lab_multilineCommand';

const myUserinterfaceXML = `<Extensions>
  <Panel>
    <Order>1</Order>
    <PanelId>wx1_lab_multilineCommand</PanelId>
    <Location>HomeScreen</Location>
    <Icon>Info</Icon>
    <Color>#FF6F20</Color>
    <Name>MultiLine Command [2.6.6]</Name>
    <ActivityType>Custom</ActivityType>
    <Page>
      <Name>Page</Name>
      <Row>
        <Name>Buttons</Name>
        <Widget>
          <WidgetId>wx1_GroupButton</WidgetId>
          <Type>GroupButton</Type>
          <Options>size=4</Options>
          <ValueSpace>
            <Value>
              <Key>GroupButton_A</Key>
              <Name>A</Name>
            </Value>
            <Value>
              <Key>GroupButton_B</Key>
              <Name>B</Name>
            </Value>
            <Value>
              <Key>GroupButton_C</Key>
              <Name>C</Name>
            </Value>
          </ValueSpace>
        </Widget>
        <Widget>
          <WidgetId>wx1_TextButton</WidgetId>
          <Name>Text</Name>
          <Type>Button</Type>
          <Options>size=1</Options>
        </Widget>
        <Widget>
          <WidgetId>wx1_IconButton</WidgetId>
          <Type>Button</Type>
          <Options>size=1;icon=green</Options>
        </Widget>
        <Widget>
          <WidgetId>wx1_SpinnerButton</WidgetId>
          <Type>Spinner</Type>
          <Options>size=2</Options>
        </Widget>
      </Row>
      <Row>
        <Name>Control Wheel</Name>
        <Widget>
          <WidgetId>wx1_ControlWheel</WidgetId>
          <Type>DirectionalPad</Type>
          <Options>size=4</Options>
        </Widget>
      </Row>
      <Row>
        <Name>Toggle and Slider</Name>
        <Widget>
          <WidgetId>wx1_Toggle</WidgetId>
          <Type>ToggleButton</Type>
          <Options>size=1</Options>
        </Widget>
        <Widget>
          <WidgetId>wx1_Slider</WidgetId>
          <Type>Slider</Type>
          <Options>size=3</Options>
        </Widget>
      </Row>
      <Options/>
    </Page>
  </Panel>
</Extensions>`


const buildUserInterface = async function () {
  try {
    const saveUI = await xapi.Command.UserInterface.Extensions.Panel.Save({ PanelId: myPanelId }, myUserinterfaceXML)
    console.log(`Panel [${myPanelId}] saved to the codec`)
  } catch (e) {
    console.error(e)
  }
}

function init() {
  countdown(delay_in_seconds);

  buildUserInterface()
}

init();
Time Source Message
HH:MM:SS [system] Runtime stopped!
HH:MM:SS [system] Using XAPI transport: WebSocket
HH:MM:SS [system] Starting macros...
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [10] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 QJS Ready
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 Panel [wx1_lab_multilineCommand] saved to the codec
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"LayoutUpdated":{"id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Panel":{"Clicked":{"PanelId":"wx1_lab_multilineCommand","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [9] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [8] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Pressed":{"Signal":"wx1_GroupButton:GroupButton_A","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"pressed","Value":"GroupButton_A","WidgetId":"wx1_GroupButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Released":{"Signal":"wx1_GroupButton:GroupButton_A","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"released","Value":"GroupButton_A","WidgetId":"wx1_GroupButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [7] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Pressed":{"Signal":"wx1_IconButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"pressed","Value":"","WidgetId":"wx1_IconButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Released":{"Signal":"wx1_IconButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"released","Value":"","WidgetId":"wx1_IconButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Clicked":{"Signal":"wx1_IconButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"clicked","Value":"","WidgetId":"wx1_IconButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [6] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Pressed":{"Signal":"wx1_ControlWheel:center","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"pressed","Value":"center","WidgetId":"wx1_ControlWheel","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Released":{"Signal":"wx1_ControlWheel:center","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"released","Value":"center","WidgetId":"wx1_ControlWheel","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Clicked":{"Signal":"wx1_ControlWheel:center","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"clicked","Value":"center","WidgetId":"wx1_ControlWheel","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [5] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [4] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Pressed":{"Signal":"wx1_Slider:188","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"pressed","Value":"188","WidgetId":"wx1_Slider","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Changed":{"Signal":"wx1_Slider:98","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"changed","Value":"98","WidgetId":"wx1_Slider","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Changed":{"Signal":"wx1_Slider:98","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"changed","Value":"98","WidgetId":"wx1_Slider","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Released":{"Signal":"wx1_Slider:98","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"released","Value":"98","WidgetId":"wx1_Slider","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [3] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Changed":{"Signal":"wx1_Toggle:on","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"changed","Value":"on","WidgetId":"wx1_Toggle","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [2] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Pressed":{"Signal":"wx1_SpinnerButton:decrement","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"pressed","Value":"decrement","WidgetId":"wx1_SpinnerButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopping in [1] seconds
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Released":{"Signal":"wx1_SpinnerButton:decrement","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"released","Value":"decrement","WidgetId":"wx1_SpinnerButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Event":{"Clicked":{"Signal":"wx1_SpinnerButton:decrement","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 {"Widget":{"Action":{"Type":"clicked","Value":"decrement","WidgetId":"wx1_SpinnerButton","id":"1"},"id":"1"},"id":"1"}
HH:MM:SS xEvents_Lesson-2_MacroPak_2-6-6 AllExtensions Subscription stopped!