Shared Variables

From SpinetiX Support Wiki

Jump to: navigation, search

Introduction

The Shared Variables mechanism was developed to allow sharing data internally (from one SVG document to another) and with external devices via IP network. It also includes a reactive component that triggers on-update events whenever the value of the Shared Variable is changed - this can be used to create interactive content.

A typical usage is to use an external device (e.g., another HMP, a web page, a telnet client, a Control System device etc.) and send "messages" to a player, which will react to those messages in a certain manner (like start playing a media file, displaying a message on the screen etc.). See more on the Interactivity via IP network page.

Another typical usage is for one SVG document to get the user action (mouse click, key press, etc.) and update a Shared Variable, while another SVG document listens for updates of that Shared Variable and performs a certain action when it is updated to a certain value. See more on the Interactivity via USB page.

Furthermore, the second SVG document could be on another player, thus the first would act as a controller for the second one.

Architecture

The "Shared Variables" mechanism is based on a client / server model whereby the HMP maintains an internal server managing a collection of variables and different clients connect to this server to:

  • subscribe / unsubscribe from receiving real-time update notifications about a certain variable from the server;
  • set the value of a certain variable;
  • send events to be executed by the server.

A client could be either internal (i.e. another SVG document) or external (e.g., another HMP, a web page, a telnet client, a Control System device etc.). To allow external clients to access the SV server, the SV Network API must be enabled on the HMP (this feature is disabled by default).

The above operations are available through two APIs: SV Network API and SV JavaScript API.

Persistence

The Shared Variables are automatically saved into localStorage, starting with 3.1.0 release, which means that changing a Shared Variable triggers a modification of the local storage data, which is kept upon the player reboot. Because of this, the Shared Variables can also be manipulated through APIs related to Web Storage.

Before 3.1.0 release, the Shared Variables were stored in a non-persistent manner on the "server" HMP, meaning that a reboot of that HMP would reset all the Shared Variables to empty values.

Web Storage bridge

Since the Shared Variables are automatically saved and persisted into localStorage object, they can also be manipulated with the following APIs:

How to update a Shared Variable

There are different ways to update a Shared Variable:

Note Note:
To update a Shared Variable on another player, either synchronize the players as explained below or use the variable name followed by '@' and the hostname or IP address of the remote HMP and, optionally, by a TCP port (if the port number on the server is different from the port number on the local host) - e.g., var@remotehost:4567.

SV Network API

The SV Network API specifies how to send commands toward the Shared Variables server on the player from another device / application. There are two server implementation: a secure and legacy (unprotected).

Note Notes:

How to enable SV Network API

To allow external clients to access the Shared Variables server, the SV Network API must first be enabled on the player, as this feature is disabled by default.

Network API settings

For HMP350 and HMP400, HMP400W, iBX410, iBX440, and third-party DSOS players with DSOS SYSTEMS license, with firmware 4.3.0 or above, follow these steps:

  1. Open Control Center and go to Advanced Applications > Network API page.
  2. Click on the checkbox labeled "Enable secure server".
  3. If the legacy server is required, click on the checkbox labeled "Enable legacy unprotected server"; change the port if needed.
  4. Click the "Apply" button and reboot the player.


Network API settings

For HMP350 devices with firmware below 4.3.0, follow these steps:

  1. Open HMP Control Center and go to Advanced Applications > Network API page.
  2. Click on the checkbox labeled "Network API".
  3. If necessary, change the default "1234" port.
  4. Click the "Apply" button and reboot the player.


For HMP200, HMP130, and HMP100 devices, follow these steps:

  1. Open HMP Control Center and go to Network Settings page > Advanced tab.
  2. Click on the checkbox labeled "Enable API server using port" option.
  3. If necessary, change the default "1234" port.
  4. Click the "Apply" button and reboot the player.


To use Elementi X as a Shared Variable server, follow these steps:

  1. Go to Settings > Network API...
  2. Click "Enable Network API".
  3. If necessary, change the default "1234" port.
  4. Click "OK"

Secure server implementation

Starting with firmware 4.3.0, a secure Shared Variable Network API, based on RFC8323 (CoAP REST API over TLS-PSK), is available.

  • The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks on the Internet of Things. The protocol is designed for machine-to-machine (M2M) applications such as building automation.
  • Transport Layer Security pre-shared key ciphersuites (TLS-PSK) is a set of cryptographic protocols that provide secure communication based on pre-shared keys (PSKs). These pre-shared keys are symmetric keys shared in advance among the communicating parties.
  • Only coaps+tcp flavor is supported. The server uses the standard coaps port number (5684), listens both for IPv4 ad IPv6 connections.

Endpoints

Two endpoints are exposed: webstorage and uievents.

webstorage

This endpoint allows updating Shared Variables, through the following operations:

GETcoaps+tcp://HMP_address/webstorage?v={key}[&Observe={0/1}]

Get the current value of the specified variable.
Returns 203 if an ETag header was present and the variable has not been modified. Otherwise, returns 205 and a payload with the current content. The ETag header is always set on the answer, has a fixed length of 8 bytes and is equal to the last modification date in microseconds since the UNIX epoch as a little-endian 64-bit integer.
The Observe option can be used for automatic notification of changes to the variable’s value - GET responses triggered by such requests always have a 205 response code. Possible values are:
  • 0 (register) adds the entry to the list, if not present;
  • 1 (deregister) removes the entry from the list, if present.

PUTcoaps+tcp://HMP_address/webstorage?v={key}

Set a new value for the variable with the current server timestamp. The request body contains the new variable value.
The response is always 204 on success.

POSTcoaps+tcp://HMP_address/webstorage?v={key}[&t={timestamp}][&x={expected}]

Modify a variable in local storage with optional timestamping and precondition. The request must include a payload with the new value.
t={timestamp} is optional; if present, it is the number of microseconds since the UNIX epoch.
x={expected} is optional; if present, it adds a precondition that the variable will be modified only if its current value is equal to the specified string.
Returns 204 on success or 412 if the precondition failed.
uievents

This endpoint allows sending a UI event to the player, through the following operations:

POSTcoaps+tcp://HMP_address/uievents

Send a UI event. The payload has one or more ASCII text lines with 3 space separated fields: timestamp, event name and event parameters.

GETcoaps+tcp://HMP_address/uievents

Always return an empty payload with 205 code but if the "observe" flag is set, notifications will be sent in the future with the same payload format as in POST above.

Synchronize multiple players

Multiple players can be synchronized together, so that Shared Variables changes on a "primary" player can be broadcasted to multiple "client"/"follower" players. This is useful for interactive content or streaming within a multiscreen content context.

For this, follow these steps:

  1. Enable the Network API on the "primary" player as detailed above.
  2. Generate a client configuration file from the primary.
  3. On each "follower"
    1. Go to Advanced Applications > Network API page
    2. Click the "Upload Client Config" button and apply that file.
    3. Once done, the IP address, usually IPv6, of the primary would appear in the "Default network API server address" field.
Debug within Elementi X

To configure Elementi as a "follower" client for debugging purposes, follow these steps:

  1. Rename the client configuration file generated from the primary as .xml instead of .cfg.
  2. Import it under Settings > Network Credentials... dialog. That shall add an entry "coaps+tcp://IP" in the list.
  3. Add that IP as "Default Server" under Settings > Network API... dialog.

Legacy server implementation

Note  
Applies to all players, except HMP300 and DiVA.

The legacy implementation provides unprotected access to the Shared Variables server through raw TCP socket connections or HTTP GET requests to the predefined end-points.

Note  
See the Shared Variables legacy server page for more details.

SV JavaScript API

The SV JavaScript API specifies how Shared Variables can be used within the JavaScript code. The API main entry point is the global function createSharedVariable() which returns a SharedVariable object that can be used to register listeners and / or set the value of Shared Variables. The global function fireSharedEvent() can be used to generate events dynamically, without using an interactive device.

The SharedVariable object is a JavaScript object containing three read-only properties (name, value and lastUpdateTime) and four methods (addUpdateListener, removeUpdateListener, set and testAndSet), further detailed below.

You can test the SV JavaScript API using any of these projects:

IDL definition

interface SharedVariable {
   readonly attribute DOMString name;
   readonly attribute DOMString value;
   readonly attribute float lastUpdateTime;
   
   void addUpdateListener( UpdateListener callback, boolean timeSync = true );
   void removeUpdateListener( UpdateListener callback );
   void set( DOMString value );
   void testAndSet( DOMString test, DOMString set );
};

void UpdateListener( SharedVariable sv );

interface SVGGLobal {
   SharedVariable createSharedVariable( DOMString name );
   void fireSharedEvent( DOMString eventName, DOMString eventParams );
};

createSharedVariable()

createSharedVariable( name );
Returns: SharedVariable Object.
Creates a SharedVariable object linked to a local or a remote Shared Variable. The method takes one parameter:
  • name
    Type: String.
    The name of the Shared Variable. The format of the name determines if it's a local or remote Shared Variable.
    To connect to a remote Shared Variable, use the variable name followed by '@' and the hostname or IP address of the remote HMP and, optionally, by a TCP port (if the port number on the server is different from the port number on the local host) - e.g., var@remotehost:4567.
Note Note:
In the background, a SUBSCRIBE command is sent to the local / remote server in order to receive notification whenever the Shared Variable is updated though any method. An implicit listener is attached to the SharedVariable object, that will asynchronously update the object properties when such a notification is received. To execute some code at the time of the update, it is recommended to register a custom listener.

fireSharedEvent()

fireSharedEvent( eventName, eventParams );
Returns: undefined.
Generates a UI event (as if an interactive device is used) by updating a special Shared Variable "%EVENTS%". The method takes two parameters:

SharedVariable object

The SharedVariable object is a JavaScript object containing three read-only properties:

  • SharedVariable.name
    Type: String.
    The name of the Shared Variable.
  • SharedVariable.value
    Type: String or Null.
    The value of the Shared Variable. Initially, this is set to null.
  • SharedVariable.lastUpdateTime
    Type: Number.
    The time when the Shared Variable was last updated, as number of milliseconds since 1 January, 1970 UTC. Initially, this is set to 0.

The SharedVariable object has four methods (addUpdateListener, removeUpdateListener, set and testAndSet), further detailed below.

It is recommended to read these properties within an update listener (see details below) or directly from the localStorage (requires firmware 3.1.0 or later) to ensure having the real values because the implicit update listener is executed asynchronously.

.addUpdateListener()

SharedVariable.addUpdateListener( callback );
SharedVariable.addUpdateListener( callback, timeSync );    // Version added: 3.0.0.
Returns: undefined.
Registers a listener that will invoke the callback function whenever the SharedVariable is updated. The method takes one or two parameters:
  • callback
    Type: Callback.
    The callback function. A reference to the SharedVariable is automatically passed as argument to the callback function.
  • timeSync
    Type: Boolean. Default: true.
    Determines whether the callback time of execution is synchronized with SharedVariable.lastUpdateTime or not (i.e., current time is used). This parameter was introduced in firmware 3.0.0 with a default value of true for backwards compatibility (before that releases, the callback time of execution was synchronized all the time), however, except for the case of synchronized content, this parameter should be set to false.

.removeUpdateListener()

SharedVariable.removeUpdateListener( callback );
Returns: undefined.
Removes a registered listener. The method takes one parameter: callback. (See definition above).

.set()

SharedVariable.set( value );
Returns: undefined.
Sets the SharedVariable.value property to a string value. If any listeners are registered, this action will trigger them.

.testAndSet()

SharedVariable.testAndSet( test, value );
Added in firmware 3.2.0.
Returns: undefined.
Sets the SharedVariable.value property to a string value if and only if the SharedVariable.value is equal to test. If any listeners are registered, this action will trigger them.

Code example

// update listener function
function onUpdate( sv ) {
    alert( sv.name + ' : ' +sv.value );
}

var v1 = createSharedVariable( 'var1' );    // create a local Shared Variable
v1.addUpdateListener( onUpdate, false );    // register the update listener
v1.set( 'value1' );                         // update the Shared Variable value


var v2 = createSharedVariable( 'var2@spx-hmp-001d50100033' );    // create a remote Shared Variable
v2.addUpdateListener( onUpdate, false );                         // register the update listener
v2.set( 'value2' );                                              // update the Shared Variable value

// simulate a mouse click at coordinates (250,20)
fireSharedEvent( "mousedown", "x=250,y=20,button=0" );
fireSharedEvent( "mouseup",   "x=250,y=20,button=0" );

Support in HTML5

It is possible to use the Shared Variables JavaScript API within web page layers, starting with DSOS 4.7.0 and Elementi 2021 Update 5. To enabled it, use the "allow" attribute (i.e., allow="spx-api") on the web page layer. Within the HTML5 code, you can create a SharedVariable object by calling the createSharedVariable function on the spx global object, something like this:

document.addEventListener("DOMContentLoaded", () => {
    let v = spx.createSharedVariable("toto");
    let e = document.getElementById("text");
    v.addUpdateListener((sv) => {
        e.textContent = `${sv.name} has new value: ${sv.value}`;
    });
    let count = 0;
    setInterval(() => {
        v.set(++count);
    }, 3000);
});

Troubleshooting

  • SharedVariable.set( value ); code is ignored.
    This could happen because the SharedVariable.lastUpdateTime property value is grater than the timestamp of the current update command. Note that when using schedules or synchronized content, the JavaScript code is executed by default "in the past" (according to the beginning time of the event / project), thus the update command also gets a timestamp in the past. To avoid this behavior, add a spx:start-at-random-time="on" attribute inside the SVG document containing the code above.

See also

This page was last modified on 19 February 2024, at 08:52.