Shared Variables
From SpinetiX Support Wiki
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:
- Web Storage REST API (Added in firmware 4.3.0)
- It allows manipulating the
localStorage
data of the player from external clients through HTTP(S) calls to predefined endpoints.
- It allows manipulating the
- RPC API
- It offers several
webstorage_***
RPC commands to interact with the data stored in thelocalStorage
. It is especially useful for cases when the communication needs to be initialized by the player itself – typical usage is when the player is behind a firewall or a NAT. - Note: For a sample script using RPC, see the "Shared Variable AJAX Updater" html script.
- It offers several
There are different ways to update a Shared Variable:
- Create content in Elementi using the interactive widgets working with Shared Variable: Click to Action, Key to Action, Action to Switch.
- Create a web application that updates Shared Variables via
- HTTP POST calls to Web Storage REST API or RPC API. See more details above.
- the legacy server implementation. This is deprecated and not recommended.
- Use the Developer Console (Elementi X only). This requires the SV Network API to be enabled on the player.
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).
- The SV Network API is not available on HMP300 and DiVA players.
- A DSOS SYSTEMS license is required on HMP400, HMP400W, iBX410, iBX440, and third-party DSOS players.
- Elementi can communicate with SV servers set on players, but only Elementi X can act as a server as well.
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.
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:
- Open Control Center and go to Advanced Applications > Network API page.
- Click on the checkbox labeled "Enable secure server".
- If the legacy server is required, click on the checkbox labeled "Enable legacy unprotected server"; change the port if needed.
- Click the "Apply" button and reboot the player.
For HMP350 devices with firmware below 4.3.0, follow these steps:
- Open HMP Control Center and go to Advanced Applications > Network API page.
- Click on the checkbox labeled "Network API".
- If necessary, change the default "1234" port.
- Click the "Apply" button and reboot the player.
For HMP200, HMP130, and HMP100 devices, follow these steps:
- Open HMP Control Center and go to Network Settings page > Advanced tab.
- Click on the checkbox labeled "Enable API server using port" option.
- If necessary, change the default "1234" port.
- Click the "Apply" button and reboot the player.
To use Elementi X as a Shared Variable server, follow these steps:
- Go to Settings > Network API...
- Click "Enable Network API".
- If necessary, change the default "1234" port.
- 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:
- Enable the Network API on the "primary" player as detailed above.
- Generate a client configuration file from the primary.
- On each "follower"
- Go to Advanced Applications > Network API page
- Click the "Upload Client Config" button and apply that file.
- 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:
- Rename the client configuration file generated from the primary as .xml instead of .cfg.
- Import it under Settings > Network Credentials... dialog. That shall add an entry "coaps+tcp://IP" in the list.
- Add that IP as "Default Server" under Settings > Network API... dialog.
Legacy server implementation
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.
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( 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
.
-
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:
-
eventName
- Type: String.
- The event name, as described under the event command above.
-
eventParams
- Type: String.
- The event parameters, as described under the event command above.
-
The SharedVariable object is a JavaScript object containing three read-only properties:
-
SharedVariable.name
- Type: String.
- The name of the Shared Variable.
-
SharedVariable.value
-
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.
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 theSharedVariable
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 oftrue
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 tofalse
.
- Type: Boolean. Default:
-
.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 stringvalue
. 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 stringvalue
if and only if theSharedVariable.value
is equal totest
. 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 aspx:start-at-random-time="on"
attribute inside the SVG document containing the code above.
- This could happen because the
See also
- Interactivity, Interactivity via IP network, Interactivity via USB
- Projects using Shared Variables: Instant messaging, JavaScript API tester, LocalStorage tester, Project:RFID.
- Add/remove a video in a multi-screen project example using Shared Variables and jSignage.