cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
47063
Views
160
Helpful
100
Replies

CE9.2.1 Macro Framework discussions

Magnus Ohm
Cisco Employee
Cisco Employee

The new Macro Framework that came with CE9.2.1 allows you to create your own small "features" that runs natively on the codec. You can also create a user interface for your features by combining the In-Room Control feature with the Macro Framework!

 

Please share your ideas, questions, code and anything else related to the newly released Macro Framework. Happy coding!

 

PS: The Macro Framework is currently not supported on the SX10 (N)

 

Please visit the official DEVNET page for ROOM DEVICES. 

https://developer.cisco.com/site/roomdevices/

100 Replies 100

I have a similar question to the above. Based on the examples, you should be able to send actual XML in the commands, but it doesn't seem to work for me. For example, I'm writing a macro to swap display roles based on content sending (a bit more advanced than the one already posted here), and I'd like to be able to send the following two commands in a single command.

 

xapi.config.set('Video Output Connector 1 MonitorRole', 'Second');
xapi.config.set('Video Output Connector 2 MonitorRole', 'First');

 

Based, on the examples, this should be able to be done via XML, in the arguement, but hasn't seemed to work for me. I'm trying the below, but the console coplains about the formatting:

 

xapi.config.set('Video', `<Output><Connector item=1><MonitorRole>First</MonitorRole></Connector><Connector item=2><MonitorRole>Second</MonitorRole></Connector></Output>`);

Any thoughts on this?

 

Additionally, does anyone know if there is a way to disable the second screen when not in use? It's quite distracting to see the same content mirrored on both displays and it would be nice to disable the 2nd screen when showing local only content. 

 

Thanks,
Sean

The question above refers to more than one argument which is solved by setting the ConnectorId value as an array. In your case you need the full base config and then pass the arguments.

 

XML wont work like that. If a multiline command takes XML input then yes but not like your example.

 

If you are worried that you have many scenarios and have to write the Config command multiple times I would write something like this:

 

const xapi = require('xapi');

const role = {1: 'First',
              2: 'Second',
              3: 'Third'};
const conn = {1: '1',
              2: '2',
              3: '3'};
  
function setRoles(roleMap) {
  for (var [key, value] of Object.entries(roleMap)) {
    xapi.config.set('Video Output Connector ' + conn[key] + ' MonitorRole', role[value]);
  }
}

xapi.event.on('', event => {
    if (event.PresentationPreviewStarted) 
      setRoles({2: 2, 1: 3});
    else if (event.PresentationStarted) 
      setRoles({2: 1, 1: 2});
});

Maybe a bad example but my point is that if you create a function that generates the command for you. The above dynamically expands to more roles as well, just insert more combinations.

Did I understand your question correctly?

 

/Magnus

Thanks for the response, Magnus! Interesting you can listen for events rather than just listen to for the status changes. Is there some place we can find all of the possible events we can set up listeners for (when SSH'd to a codec, you can't tab/? out items in the xevent category)?

 

My original questions was more to increase the speed of the change, rather than serially issues the commands one after another to send the updates as a single command like we can do with the XML API.

 

Additionally, I'm assuming monitor role 3 is no video, but is a role limited to SX80s? I don't see that as an option on the SX20 or Room Kit Plus.

 

Here's the code I have that's working, but it's definitely not going to be as efficient as your example listening for the actual events.

 

const xapi = require('xapi');

function presentationMode(sending) {
  if (sending === true) {
    xapi.config.set('Video Output Connector 1 MonitorRole', 'Second');
    xapi.config.set('Video Output Connector 2 MonitorRole', 'First');
  }
  else {
    xapi.config.set('Video Output Connector 1 MonitorRole', 'First');
    xapi.config.set('Video Output Connector 2 MonitorRole', 'Second');
  }
}

function checkStatus(status) {
  console.log('status change', status);

  // first check if codec is sending or receiving content in a call
  if (status.Mode) {
    if (status.Mode === 'Sending' || status.Mode === 'Receiving' ) {
      presentationMode(true);
    }
    if (status.Mode === "Off") {
      presentationMode(false);
    }
  }
  else if (status.LocalInstance) {
    if (status.LocalInstance[0].SendingMode === 'LocalOnly') {
      presentationMode(true);
    }
    else if (status.LocalInstance[0].ghost === 'True') {
      presentationMode(false);
    }
  }
}

// Event listener for all presentation mode changes
xapi.status.on('Conference Presentation', checkStatus);

Thanks,
Sean

For SX20 no, for SX80 yes as you already guessed, but I put that in there just as an example.

You see the base events in the xEvents command but the event can have a lot of metadata. The event.PresentationStarted has metadata for example. Here is a simple example how to capture the data for a specific event:

 

 

const xapi = require('xapi');

xapi.event.on('PresentationStarted', event => {
  console.log(JSON.stringify(event));
});

Produces and output of the following if presentation is started in a call:

'{"id":"1","Cause":"userRequested","ConferenceId":"6","Mode":"Sending","CallId":"1","LocalInstance":"1","LocalSource":"2"}'

This data I can use upon an event. If event.Mode == 'Sending' do whatever. Just see what data it spits out in the different events.

 

Its pretty much the same as registering an xFeedback in the xAPI but the macros are more practical:

xfeedback register event/presentationstarted
** end

OK
*e PresentationStarted Cause: userRequested
*e PresentationStarted ConferenceId: 6
*e PresentationStarted Mode: Sending
*e PresentationStarted CallId: 1
*e PresentationStarted LocalInstance: 1
*e PresentationStarted LocalSource: 2
** end

xFeedback is very nice to test the events to see what it spits out without creating a macro. Its quick and easy. Then you may see that PresentationStarted has the Mode which might be the one interesting to you, you just want to listen to that event:

 

const xapi = require('xapi');

xapi.event.on('PresentationStarted Mode', event => {
  console.log(JSON.stringify(event));
});

Outputs:

'"Sending"'

Hi Magnus - I've got one related to this I'm hoping you or someone can assist with...

Rather than swapping around monitor roles, we want to enable/disable HDMI output 3 on an SX80 via a Macro, but haven't been able to find a way to do it.

The use case is a combined/split room scenario whereby the third screen should either be off or have no video signal when the rooms are "split".  We've managed to work out the audio etc using macros but haven't found a way to disable the third HDMI output when required.

Any ideas?

Afaik there are no options to turn off the HDMI output connectior via the API (video) on demand. If the TV has some kind of API where you can turn it off and on I guess that could be something to consider?

/Magnus

Thanks Magnus, we'll look into that.

This can be solved using the below example:

 

const xapi = require('xapi');

function unsetGUIValue(guiId) {
    xapi.command('UserInterface Extensions Widget UnsetValue', {
        WidgetId: guiId
    });
}
function compose(sources, parts) {
    const layout = (parts[2] === 'e') ? 'equal':'pip'
    xapi.command('Video Input SetMainVideoSource', {
        ConnectorId: sources,
        Layout: layout
    }).catch(e => {
      xapi.command('UserInterface Message TextLine Display', {
        Text: e.message,
        duration: 3
      });
    });
}
xapi.event.on('UserInterface Extensions Widget Action', e => {
    if (e.Type == "released") {
      const parts = e.WidgetId.split('_');
      const sourcecombo = e.Value.split('-');
      compose(sourcecombo,parts);
      unsetGUIValue(e.WidgetId);
    }
});

The above takes the WidgetID (which is named accordingly so it can be parsed by the code) and generates the source combo and the layout.

 

An example WidgetID can be:

 

 <Widget>
          <WidgetId>s_2_e_3</WidgetId>
          <Type>GroupButton</Type>
          <Options>size=4</Options>
          <ValueSpace>
            <Value>
              <Key>3-1</Key>
              <Name>S3-S1</Name>
            </Value>
            <Value>
              <Key>3-2</Key>
              <Name>S3-S2</Name>
            </Value>
            <Value>
              <Key>3-4</Key>
              <Name>S3-S4</Name>
            </Value>
          </ValueSpace>
        </Widget>

This way I can just create more buttons in the In-Room Control editor without touching the Macro code.

 

/Magnus

Right after macros were introduced, we created one to help our users dial meetings at our custom webex url. They push a button, it pops up a dialog box asking for the 9 digit meeting ID, then the script appends @ourcustomdomain.webex.com to the end and dials it as a SIP call.

 

This was working just fine until CE 9.5.0 and/or Audio Console, which I noticed creates its own macro. I have had complaints from users in three different rooms so far saying that pressing the 'dial video' button doesn't pop up the dialog box anymore. I think that the only thing these rooms have in common is that the codecs are running 9.5.0 and we've probably used the audio console to configure all of them.

 

Panel:

<Extensions>
  <Version>1.4</Version>
  <Panel>
    <Icon>Language</Icon>
    <Type>Home</Type>
    <Page>
      <Name>Citadel WebEx Shortcut</Name>
      <Row>
        <Name>Click to Dial Citadel WebEx:</Name>
        <Widget>
          <WidgetId>dial_webex_video_number</WidgetId>
          <Name>Dial Video by WebEx Meeting ID</Name>
          <Type>Button</Type>
          <Options>size=4</Options>
        </Widget>
        <Widget>
          <WidgetId>dial_webex_audio</WidgetId>
          <Name>WebEx Audio Only</Name>
          <Type>Button</Type>
          <Options>size=4</Options>
        </Widget>
      </Row>
      <PageId>citadel_webex</PageId>
      <Options/>
    </Page>
    <Name>WebEx</Name>
  </Panel>
</Extensions>

Macro:

const xapi = require('xapi');
// These match the widget ids of the In-Room control buttons
const numbers = {
  dial_webex_video: 'meet@citadelmeetings.webex.com',
  dial_webex_audio: '2000',
};
function dial(number) {
//  console.log('dial', number);
  xapi.command('dial', { Number: number });
}
function listenToGui() {
  xapi.event.on('UserInterface Extensions Widget Action', (event) => {
    if (event.Type === 'clicked') {
		if (event.WidgetId === 'dial_webex_video_number') {
		// Show the number entry screen
			xapi.command(' UserInterface Message TextInput Display', {FeedbackId: 'webex_meeting_id', InputType: 'Numeric', Placeholder: '123456789', SubmitText: 'Dial Webex', Text: 'Enter your 9-digit meeting ID. This will only work for @citadelmeetings.webex.com IDs.', Title: 'Enter Citadel WebEx ID'});
		}
		else
		{
			const number = numbers[event.WidgetId];
			if (number) dial(number);
			else console.log('Unknown button pressed', event.WidgetId);
		}
	}
	});

	xapi.event.on('UserInterface Message TextInput Response', (event) => {
		if (event.FeedbackId === 'webex_meeting_id') {
			const constructed_number = event.Text + '@citadelmeetings.webex.com';
	//		console.log('Trying to dial meeting: ', constructed_number);
			dial(constructed_number);
		}
	});
}
listenToGui();

Your thoughts and feedback are appreciated!

 

Never mind! See https://bst.cloudapps.cisco.com/bugsearch/bug/CSCvm92418/

In the mean time I hope the script example was helpful for folks.

That bug was fixed in CE9.5.1, it just has not updated yet.

 

/Magnus

Hello,

We're trying to code macros with SX80. We have created a panel on the Touch10 with 2 state-less buttons, called 'renater' and 'webex'. When I press one of these buttons, I'm trying to show a message on screen.

The "event.Type === 'clicked'" is perfectly cpatured, but when it enters the "if ( event.WidgetID === 'renater') {....}", it seems that WidgetID is not captured.

Here is the code :

const xapi = require('xapi');


function showOnScreen(title, text, duration = 5) {
  xapi.command('UserInterface Message Alert Display', {
    Title: title,
    Text: text,
    Duration: duration,
  });
}

function switchDefaultProtocol(event) {
 
  if (event.Type === 'clicked') {
       if (event.WidgetID === 'renavisio' ) {
        const msgSIP = 'SIP';
        showOnScreen('Default Call Protocol:', msgSIP);
     }
 
     if (event.WidgetID === 'webex' ) {
       const msgWBX = 'WEBEX';
       showOnScreen('Default Call Protocol:', msgWBX);
    }


  }
 
}

xapi.event.on('UserInterface    Extensions    Widget    Action',    switchDefaultProtocol);

What are we doing wrong ?

Hi

You have a typo in the WidgetID if you change this to event.WidgetId (WidgetID -> WidgetId) it should work.

/Magnus

Oh... well... Sorry, I apologize for such a newbie error.
Thank you so much

No need to apologize, everyone make these kind of mistakes. Please let us know if you have issues with more scripts, I will gladly help you debug it. I never categorize questions as "newbie", remember that the best way to learn is to make mistakes. :) Happy coding!