Skip to content

Developer Guide and Hints

Timer Widget

Timers, countdowns and clocks are challenging to be handled in the web especially when you want to synchronize them over various devices. The Singular Timer Widget is built to simplify creating and controlling timers, countdowns and clocks. Check out the Timer Widget Tutorial here to learn how to use the widget within the Singular’s Composer. The Timer Widget uses the Singular Timer Object defined in the Singular Timer JavaScript library. The Timer Object works “state-based”. It uses a 'run state' to identify whether the timer is running or paused and a combination of 'absolute start time' plus an 'offset' to calculate the current timer value. You can control the Timer Widget by sending following payload structure using the REST API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[{
  "compositionName": "Timer SubComp", // name of subcomposition
  "controlNode": {
    "payload": {
      "Timer Control": {              // name of control node
        "UTC": 1572947814783,         // UTC start time
        "isRunning": false,           // run state true/false
        "value": 0                    // offset
      }
    }
  }
}]

The Singular Release in November 2019 introduces control commands to start, pause and reset timers.

Start Timer: Send the following payload to start or continue the timer from a given time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[{
  "compositionName": "Timer SubComp", // name of subcomposition
  "controlNode": {
    "payload": {
      "Timer Control": {              // name of control node
        "command": "play"             // command to start timer
      }
    }
  }
}]

Pause Timer: Send the following payload to pause the timer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[{
  "compositionName": "Timer SubComp", // name of subcomposition
  "controlNode": {
    "payload": {
      "Timer Control": {              // name of control node
        "command": "pause"            // command to pause timer
      }
    }
  }
}]

Reset Timer: Send the following payload to stop and reset the timer to the initial value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[{
  "compositionName": "Timer SubComp", // name of subcomposition
  "controlNode": {
    "payload": {
      "Timer Control": {              // name of control node
        "command": "reset"            // command to reset timer
      }
    }
  }
}]
  • Download the Postman Collection here.
  • Example Studio Control here.
  • Example Output URL here.

HTTP GET Request

Define Request URL and Credentials

1
2
3
4
5
6
7
8
/**************************************************************************
 * prepare REST request
 */
function sendRestGet() {
  var requestURL = "https://app.singular.live/apiv1/appinstances/";
  var credentials = btoa("username" + ":" + "password");
  singularRestGET(requestURL, credentials);
}

GET request unsing fetch()

Note: GET doesn't require a request body.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**************************************************************************
 * singularRestGET
 */
function singularRestGET(url, auth) {
  var responseMessage;
  var options;

  // setup the HTTP call
  options = {
    method: "GET",
    contentType: "application/json",
    mode: "cors",
    headers: {
      Authorization: "Basic " + auth
    }
  };

  fetch(url, options)
    .then(httpResponse => {
      if (httpResponse.ok) {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
        return httpResponse.json();
      } else {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
        return Promise.reject("Fetch did not succeed");
      }
    })
    .then(json => {
      console.log(json);
      document.getElementById("responseJSON").innerHTML = JSON.stringify(json, undefined, 2);
    })
    .catch(err => {
      console.log("ERROR: " + err);
    });
}
/**************************************************************************/

HTTP POST Request

Define Request URL and Credentials

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**************************************************************************
 * prepare REST request
 */
function sendRestPost() {
  var resourceURL = "/apiv1/appinstances/";
  var requestURL = "https://app.singular.live/apiv1/appinstances/";
  var credentials = btoa("username" + ":" + "password");
  var requestBody = {
    folder: "4e867728-b556-4d10-8761-afecf431614b",
    apptemplate_id: "57",
    apptemplate_version_status: "published",
    name: "New Studio Control (composition specified)",
    compositionList: [
      {
        name: "Comp-for-DevDocs",
        composition: {
          refId: "85834"
        }
      }
    ],
    account_id: "56",
    user_id: "2290",
    shareurl: "0"
  };
  singularRestPOST(requestURL, credentials, requestBody);
}

POST request unsing fetch()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**************************************************************************
 * singularRestPOST
 */
function singularRestPOST(url, auth, body) {
  var responseMessage;
  var options;

  // setup the HTTP call
  options = {
    method: "POST",
    mode: "cors",
    headers: {
      Authorization: "Basic " + auth,
      "content-type": "application/json"
    },
    body: JSON.stringify(body)
  };

  fetch(url, options)
    .then(httpResponse => {
      if (httpResponse.ok) {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
        return httpResponse.json();
      } else {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
        return Promise.reject("Fetch did not succeed");
      }
    })
    .then(json => {
      if (json != undefined) {
        console.log(json);
        document.getElementById("responseJSON").innerHTML = JSON.stringify(json, undefined, 2);
      }
    })
    .catch(err => {
      console.log("ERROR: " + err);
    });
}
/**************************************************************************/

HTTP PUT Request

Define Request URL and Credentials

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**************************************************************************
 * prepare REST request
 */
function sendRestPut() {
  var resourceURL = "/apiv1/appinstances/{app_instance_id}/control";
  var requestURL = "https://app.singular.live/apiv1/appinstances/36766/control";
  var credentials = btoa("username" + ":" + "password");
  var requestBody = [
    {
      compositionName: "Lower Line",
      animation: {
        action: "play",
        to: "In"
      }
    }
  ];
  singularRestPUT(requestURL, credentials, requestBody);
}

PUT request unsing fetch()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**************************************************************************
 * singularRestPUT
 */
function singularRestPUT(url, auth, body) {
  var responseMessage;
  var options;

  // setup the HTTP call
  options = {
    method: "PUT",
    mode: "cors",
    headers: {
      Authorization: "Basic " + auth,
      "content-type": "application/json"
    },
    body: JSON.stringify(body)
  };

  fetch(url, options)
    .then(httpResponse => {
      if (httpResponse.ok) {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
      } else {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
        return Promise.reject("Fetch did not succeed");
      }
    })
    .then(json => {
      if (json != undefined) {
        console.log(json);
        document.getElementById("responseJSON").innerHTML = JSON.stringify(json, undefined, 2);
      }
    })
    .catch(err => {
      console.log("ERROR: " + err);
    });
}
/**************************************************************************/

HTTP DEL Request

Define Request URL and Credentials

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**************************************************************************
 *
 */
function sendRestDelete() {
  var resourceURL = "/apiv1/appinstances/{app_instance_id}";
  var requestURL = "https://app.singular.live/apiv1/appinstances/44361";
  var credentials = btoa("username" + ":" + "password");
  var requestBody = null;
  singularRestDELETE(requestURL, credentials);
}

DELETE request unsing fetch()

Note: GET doesn't require a request body.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**************************************************************************
 * singularRestDELETE
 */
function singularRestDELETE(url, auth) {
  var responseMessage;
  var options;

  // setup the HTTP call
  options = {
    method: "DELETE",
    mode: "cors",
    headers: {
      Authorization: "Basic " + auth,
      "content-type": "application/json"
    }
  };

  fetch(url, options)
    .then(httpResponse => {
      if (httpResponse.ok) {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
        return httpResponse.json();
      } else {
        responseMessage = httpResponse.status + "   " + httpResponse.ok + "   " + httpResponse.statusText;
        document.getElementById("responseStatus").innerHTML = responseMessage;
        return Promise.reject("Fetch did not succeed");
      }
    })
    .then(json => {
      if (json != undefined) {
        console.log(json);
        document.getElementById("responseJSON").innerHTML = JSON.stringify(json, undefined, 2);
      }
    })
    .catch(err => {
      console.log("ERROR: " + err);
    });
}
/**************************************************************************/