# Understanding Signals

The Captivate backend exposes a variety of “signals” that you can connect to for getting instant updates from the Captivate backend.

## Signals

### connecting

To connect to one of these signals, call the connect method (i.e. `scheduler.[SIGNALNAME].connect`) on the desired signal and provide a callback that will handle the signal data. Signal data will be different for each signal and is specified in the list below.

### disconnecting

Signals expose a `disconnect` method you can use to disconnect your callback from a signal, but our library makes it easier for you by returning a "canceler" function that you can use to disconnect your callback from the signal. Using the canceler function allows you to use an anonymous function for the callback and still be able to disconnect it from the signal later.

```javascript
const canceler = SeriviceHandler.scheduler.onNotify(() => {
  console.log('we got a notification!!');
});
// ... some time later
canceler(); // all cleaned up
```

> **Note:** If you call the connect method with an anonymous function, you must use the returned canceler function. There won't be a way to call disconnect with the same function in the future because it was anonymous.

The most useful of all the signals is `onNotify` since it allows you to receive updates whenever a title’s play status or data variables change. Use this in combination with a `subscribe` command according to the example below.

* `onNotify` - Calls the callback with a JSON-encoded string of notification data.
  * Includes data requested from a previous `subscribe` command.
  * Includes all commands passing through the command bus.
* `newCommandXML` - Calls the callback with an XML document that represents the last command executed in the system.
* `messageIn` - When another controller calls the `messageOut` method, the connected function will be called with three arguments: `from`, `to`, `data` where `data` is the JSON-encoded data from the message.
* `redirected` - Informs when the HTML page in the Data Controller is redirected (rarely used).
* `serverPortChanged` - When server ports were changed (rarely used).
* `settingsChanged` - The connected function will be called with the `inputName` as the first argument whenever any controller settings are saved. If you get this, you should check to see if the `inputName` matches your controller and then maybe request the latest settings using `loadSettings`. *NOTE: Beware of creating infinite loops.*
* `variablesChanged` - The function you connect will be called with the `inputName` as the first argument (or `"all"` if an input name was not specified in the update command) and a map of changed variables as the second argument.
  * This function doesn’t work entirely as expected. It was originally designed to keep controller UI in sync with the true state of the Captivate system, but it only reports *controller* variable changes made through API calls. Remember that editing a variable value from the program UI (Live Data panel, Values Grid, etc) directly affects the *graphic* variable and therefore doesn’t trigger this callback. If you want to keep track of *graphic* variable updates, you should `subscribe` to the `data` event.

### Example:

```javascript
/**
 * Because there is a single server for all HTML inputs, this callback will be fired for
 * *any* variable change and will have data that might not be relevant to this controller,
 * so be sure to filter on the input name.
 */
scheduler.variablesChanged.connect((inputName, variables) => {
  if (inputName == INPUT_NAME) {
    console.log('variables changed', variables);
  }
});
```

## Full Notification Example with Subscription

To make full use of the `onNotify` signal, you need to also understand the `subscribe` command, so this example shows how to do that

### Example:

```javascript
// assuming the scheduler is already connected!!

const my_name = 'My Subscriber';
const my_input = ServiceHandler.inputName;
const scheduler = ServiceHandler.scheduler;

/**
 * The `subscribe` command tells Captivate to register a new "subscription" or modify an existing one.
 *
 * Internally, subscriptions are identified by the value sent as the "sender" and many data controllers
 * leave this value blank to share the existing notification sender in the system.
 *
 * However, if you want to control what your data controller sees, it's helpful to specify your own sender
 * and customize the events, titles, and/or inputs that send notifications.
 *
 * If a subsequent "subscribe" command is sent with the same sender name, any new parameters will be
 * appended to the existing ones.
 *
 * If you register a subscription with a sender, it's good practice to make sure you later send an
 * unsubscribe command with that same sender name and event list so it gets cleaned up inside Captivate
 *
 * The following command will create a new sender identified by `my_name` and will ask for all
 * play and data notifications that relate to the specified input.
 */
scheduler.scheduleCommand('subscribe', { input: my_input, events: 'play,data', sender: my_name }, {});

// setup the callback
const disconnect = scheduler.onNotify.connect((messageText) => {
  // Convert the payload string into a JavaScript object.
  let data = JSON.parse(messageText);

  // The WebSocket API sends all notifications to all controllers,
  // so ignore the ones we don't want.
  if (data.sender != my_name) return;

  // log the data
  console.log(data);
});

// connect the callback
scheduler.onNotify.connect(handler);

// ... some time later... to cleanup
disconnect();
scheduler.scheduleCommand('unsubscribe', { sender: my_name, events: 'play,data' }, {});
```

### A Note About Notification Senders

The sender name you specify in the subscribe command creates a subscription object in the Captivate backend. You can modify it by adding or removing event subscriptions as you wish. This can also be useful in your message handling code to make sure you are only paying attention to messages that come from the sender you are interested in. However, the tight coupling of sender and notification messages only happens with the [External API connection methods.](https://developers.newbluefx.com/captivate-api/advanced-api-reference)

The WebSocket API, for legacy reasons, currently sends all notification messages to all WebSocket clients regardless of what sender they have subscribed to and it does this for every inter-process message whether the target input can be found or not. Since the other API connection methods are more selective with their message passing, the WebSocket API might move in that direction in future releases. Therefore, for your controllers, it’s good practice to specify a sender name for all subscribe operations, and to have your notification handler discard all messages that don’t come from that sender.
