How To Articles > How to Trend OPC Server Data with a REST API

How to Trend OPC Server Data with a REST API

How to Trend OPC Server Data with a REST API


The OAS REST API is a programmatic interface that allows you to Trend Real Time and Historical OPC Server data via JSON over HTTP. This tutorial walks you through downloading and installing OAS, configuring an OPC Server driver, configuring tags and using the REST API to trend your OPC Server Data. We will build a sample JavaScript Web Interface to demonstrate trending OPC Server data.

Step 1. Download and Install the Open Automation Software and Start the OAS Service

If you have not already done so, you will need to download and install the OAS platform.  Fully functional trial versions of the software are available for Windows, Windows IoT Core, Linux, Raspberry Pi and Docker on our downloads page.

On Windows run the downloaded Setup.exe file to install one or more of the Open Automation Software features. Select the default Typical installation if you are not sure what features to use or the Custom installation if you want to save disk space on the target system.  When prompted agree to the End User License Agreement to continue the installation.

For more detailed instructions and video tutorials, visit the installation guide for your system:
Windows Installation | Linux Installation | Raspberry Pi Installation | Dockers Installation

The OAS Service Control application will appear when the installation finishes on Windows.  Use this application to start the 3 Services. Run the Configure OAS application on Windows and select Configure-Tags; if the first time running, the AdminCreate utility will run to create an Administrator login as shown in Step 1 of Getting Started – Security.


Step 2. Configure OPC Server Tags

OAS provides multiple ways to add and define tags:

To add a Tag manually:

  1. In the OAS Configure Application, select Configure >> Tags from the top menu.


  2. Select localhost or the remote service you wish to modify with the Select button to the right of the Network Node list.


  3. Click on the Add Tag button located at the top of the Tag browser on the left portion of the screen.


  4. A dialog box will appear. Enter a name for your new tag and click ok.

  5. A configuration screen will appear for your new tag. Select OPC in the Data Source dropdown box.


  6. Use the Browse button to the right of the OPC Item to browse OPC Servers for the desired OPC Item.


  7. Select Local, the desired OPC Server, branch within the OPC Server, and OPC Item and click OK.


  8. Specify the desired OPC Update Rate for the Tag.

  9. Click Apply Changes at the bottom right of the window.

To add Tags with One Click OPC:

  1. In the OAS Configure Application, select Configure >> Tags from the top menu.


  2. Select localhost or the remote service you wish to modify with the Select button to the right of the Network Node list.


  3. To begin the One Click OPC process select the Group you would like to import to in the Tag configuration. If you want to import to the Root Level, select the Tags Group at the top of the tree. Then select the One Click Import button on the top menu bar.


  4. Click on the Import OPC DA 2.XX or 3.0 Items Button in the pop up window.


  5. Use the One Click OPC Wizard to browse for a branch as a starting position within an OPC Server or just select the OPC Server name itself to add all items from the selected OPC Server. For the best networking design select OPC Servers from Local even if you are configuring a remote OAS Service.


  6. Select to enable the options to Get Data Type from OPC Server and optionally the Descriptions. Additionally if you want to specify to Trend all of the points select Trend Points.

  7. Click Add Tags and it will automatically add all of the OPC Items from the OPC Server Branch you have selected and all of the sub Branches beneath it.

  8. Select the Save button on the toolbar at the top.

For more detailed instructions on Configuring OPC Server Tags, visit our Getting Started OPC tutorial or the One Click OPC tutorial or watch the video tutorial below:


Step 3. Access Real Time and Historical Trend Data with the REST API

How to Access Real Time and Historical Trend Data with the OAS REST API


We are going to discuss how to trend live and historical data with the OAS REST API. The interface below is what we will be building. You can see the live version of it here: http://www.opcweb.com/examples/restexamples/trend.htm.

In order for a tag to be available for trending, the Trend Point check box in the tag configuration needs to be set to true. This lets the OAS service know that it should buffer that data on the hard drive to be available for trending. The amount of time the service buffers those tags is by default 24 hours. You can change this setting under Configure >> Options >> Trending >> Longest Real Time Timeframe. Historical trends are often confusing to OAS users. In order for a tag to be available for an historical trend, it needs to have trending enabled and also be being currently logged with our Data Historian. The tags values have to have been recorded to a database for the time period involved in the historical trend.

Getting Started

To use the OAS REST API you must make sure that the OAS HTTP service is listening on the correct port. To do this, open the OAS Configuration application and select Configure > Options, then select the network node (localhost if working on the local machine) and click Select. Under the Networking tab, locate the field for REST API/WebHMI Port Number. The default is 58725 but can be changed. If you are accessing the server from a remote client, you will also need to make sure your machine and/or company firewalls allow TCP traffic on the selected port.

You can find full documentation for the OAS REST API here: http://restapi.openautomationsoftware.com as well as a link to open it in Postman.

Setting up the Page

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
    <title>REST Example | Trend</title>
    <link rel="stylesheet" href="client.css">
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script type="text/javascript">

Above is the start of the head tag and a link to the jQuery library. We will be using jQuery for this tutorial.

var networkNode = "http://www.opcweb.com:58725"; // variable for the network node
var clientid =  ""; // holds the client id from authorization
var token =  "";    // holds the token from authorization
var polling = null; // variable for setInterval function
var drawheader = true; // flag for whether the table header needs to be drawn
var currentlist = {
      "tags":[
           {"tag": "Random.Value"},
           {"tag": "Ramp.Value"},
           {"tag": "Sine.Value"}
      ],  
     "samplerate": 1, // rate of data update in seconds
     "timeframe": 100 // window of time in seconds
};

Next we declare some variables that we will use later. The networkNode is the URL for where the OAS Service you are calling is located. The clientid and the token are variables that we will use to hold the authentication credentials that will be returned in our first call. Polling is a variable we will use to hold the setInterval function for our repeat calls to the API to get the trend data. The drawheader is a flag that we set to let us know if we need to draw the header of our display again as we are polling. The currentlist object holds the data we are going to send to the API later: the tags that we want to trend, the samplerate and the timeframe.

// function to clear out variables for a reset
function clearvariables(){
    drawheader = true;
    currentlist.history = false;
    delete currentlist.id;                
    clearInterval(polling);
    polling = null;
    $("#createtrendlist").prop("value", "Create Trend List");   // toggle Create button
    $("#dopolling").prop("value", "Start Polling"); // toggle Start button
    $("#tbtrend").empty(); // clear out display 
    $("#diverror").empty(); // clear out display 
    $('#displaythis').empty(); // clear out display 
    $('#divtrendlistid').empty(); // clear out display 
}        

The clearvariables() function above does just that. We will call it later in our application to reset our button text and reset our variables when needed.

        // function to display error messages 
        function displaymessage(mess, fnc){
            clearInterval(polling); // in the event of an error we stop the polling
            switch(mess) {
                case 401:
                    $('#diverror').html("Authentication required.");
                    break;
                case 404:
                    if((fnc == "delete")||(fnc == "update")){
                        $('#diverror').html("Tag List not found.");
                    }else{
                        $('#diverror').html("404"); 
                    }
                    break;
                default:
                    $('#diverror').html(mess);
                }                       
        }

The displaymessage() function is called when our API request returns with an error. It will display the message on the page.

Authenticate

$("#doauth").click(function(){ // click funtion for authenticatation
    clearvariables(); // clear out the old variables, we are starting over   
    // api call for authenticatation
    $.ajax({
        url: networkNode + "/OASREST/v2/authenticate",
        type: "POST",
        contentType: "application/json; charset=utf-8", 
        crossDomain: true,
        dataType: "json",
        data: '{"username": "", "password": ""}',
        success: function(r) {
            clientid = r.data.clientid; // store the client id for later calls
            token = r.data.token;  // store the token for later calls      
            $('#divid').html(clientid); // display the client id
            $('#divtoken').html(token); // display the token
        },
        error: function (e) {
            displaymessage(e.status, "auth"); // in case of error, display the error         
        }
    });
});

The first call you will always need to make is to Authenticate. This will create a session and return a clientid and token that you will send in the header of all of your subsequent calls. The API call above is inside of a click function that handles the click event for the Authenticate button. The first thing we do is call the clearvariable() function in case the user has been using the application already. When the session is created it creates a new clientid and token. Anything that may have been done previously on the page will be tied to the old session and no longer be accessible so we clear out the old display as well.

We are using the jQuery ajax() method to send an asynchronous HTTP request to the server. The first parameter url specifies the address we are sending our request to. The networkNode we set above is used here. The Authenticate call is a POST which we specify in the type parameter. Next, contentType: application/json; charset=utf-8 designates the content to be in JSON format, encoded in the UTF-8 character encoding. The crossDomain parameter is set to true, allowing us to send our request to a resource that is outside of our own domain. The dataType parameter is set to “json”, letting the server know we expect the response to come back in JSON format. In out data parameter, what we are sending to the server, we have two variables: username and password. In this example, they are empty strings which will allow us to create a session in the default security group. For more information about security groups, see the Getting Started – Security tutorial in our knowledge base.

If we were to run the Authenticate function successfully here is what would be returned:

{
    "status": "OK",
    "data": {
        "clientid": "e90c8ae8-6b12-4690-a02b-f35ad03b3d2d",
        "token": "f16c3098-b295-4572-9a6b-f53ee984d21b"
    },
    "messages": [
        "Default credential provided - access to data and operations may be limited"
    ]
}

In which case, we process the return inside of our success function. We set the clientid page variable to the clientid property of the data object of the return object and the token page variable to the token property. If an error were to be returned, we handle it in our error function where we send the message to the displaymessage() function.

Create or Delete Trend List

// click event for create or deleting trend list
$("#createtrendlist").click(function(){
    if (!currentlist.id){ // if the list doesn't exist, create one
        currentlist.history = false; // reset the history flag, this is real time
        // api call to create the trend list
        $.ajax({
            url: networkNode + "/OASREST/v2/trendlists",
            type: "POST",
            contentType: "application/json; charset=utf-8", 
            crossDomain: true,
            dataType: "json",
            headers: {"clientid": clientid, "token": token},
            data: JSON.stringify(currentlist), // format and set the data for the call
            success: function(r) {
                currentlist.id = r.data.id; // store the trend list id
                $('#diverror').empty();  // we were successful, clear error display                
                $('#divtrendlistid').html(currentlist.id); // display the trend list ID
                $("#createtrendlist").prop("value", "Delete Trend List"); // toggle the create/delete button
            },
            error: function (e) {
                displaymessage(e.status, "create");  // in case of error, display the error                  
            }
        });
    }else{ // if the list exists, delete it
        // api call to delete the trend list
        $.ajax({
            url: networkNode + "/OASREST/v2/trendlists/" + currentlist.id,
            type: "DELETE",
            contentType: "application/json; charset=utf-8", 
            crossDomain: true,
            dataType: "json",
            headers: {"clientid": clientid, "token": token},
            success: function(r) {     
                clearvariables() // successful, clear out variables and display
            },
            error: function (e) {
                displaymessage(e.status, "delete");  // in case of error, display the error                  
            }
        });
    }
});

In the click event, $(“#createtrendlist”).click(function(), we handle the Create Trend List/Delete Trend List button click. First, we check to see if a trend list has already been created: if (!currentlist.id). If one has not, then we create one. Then, we set the currentlist.history to false because in this instance the user has clicked the Create Trend List button not the Create Historical Trend List button. Next, is our API call, it is a “POST”, we include the clientid and the token that we set in our Authenticate function in the headers. Before we pass the currentlist object that holds our trend list information in, we format it with the JSON.stringify() method that converts a JavaScript object or value to a JSON string. In the success function, we set the currentlist.id to the trend list id that that server returns (r.data.id), display the trend list id on the page, clear the error display because we were successful and toggle the button text. In the event of an error, we display the message.

If the currentlist.id already exists, we know the user has clicked the Delete Trend List button, so we call the API with “DELETE” type. The url in this call has the currentlist.id appended to it so that the server knows which list we are deleting. We include the clientid and the token in the headers and we have no data parameter. In the success function, we call the clearvariables() function to handle resetting the page because the user is starting over. In the event of an error, we display the message.

Get Trend List

// function to get and display the data for the trend list
function gettrendlist(){
    var table = $("#tbtrend");
    if (drawheader){ // only draw the header the first time through
        var th = "<tr>";
        th += "<th>Time Stamp</th>";  // loop trough the tags and draw the header
        for (var i  = 0; i < currentlist.tags.length; i++){
            th += "<th>" + currentlist.tags[i].tag + "</th>"
        }
        th += "</tr>";
        table.append(th);
        drawheader = false;  // set flag to false so we don't redraw header
    }
    // api call to get the data for the currentlist
    $.ajax({
        url: networkNode + "/OASREST/v2/trendlists/" + currentlist.id,
        type: "GET",
        contentType: "application/json; charset=utf-8", 
        crossDomain: true,
        dataType: "json",
        headers: {"clientid": clientid, "token": token},
        success: function(r) {
            var d = new Date(r.firsttime);  
            var interval = (r.lasttime - r.firsttime) / r.count;  // get the time interval based on first and last time and the number of data points returned
            for (var i = 0; i < r.count; i++){  // loop through tags and display the and draw the rows
                var ts = new Date (r.firsttime + (interval * i));
                    var str = "<tr>";
                    str += "<td>" + fDate(ts) + "</td>";
                    for (var j = 0; j < r.data.length; j++){
                        str += "<td>" + r.data[j][i] + "</td>";                                    
                    }
                str += "</tr>";
                table.append(str);  // add the row to the table
                if (currentlist.history){  // if we are displaying historical data, stop the polling once we get the data
                    clearInterval(polling);
                    polling = null;
                }                            
            }                                             
        },
        error: function (e) {
            displaymessage(e.status, "gettrend");  // in case of error, display the error                  
        }
    });
}
// click function to toggle polling
$("#dopolling").click(function(){
    if (!currentlist.id){ // check to see if a trend list exists.  If not, exit.
        displaymessage("Trend List not found", "poll");
        return;
    }
    if(currentlist.history){  // check to to see if it is a historical trend.  If so, exit.
        displaymessage("Polling not available for Historical Trend", "poll");
        return;
    }
    if (polling == null){  // if not polling, start the polling
        polling = setInterval(gettrendlist, 3000);
        gettrendlist();
        $("#dopolling").prop("value", "Stop Polling"); // toggle the button text
    } else {
        clearInterval(polling);
        polling = null; // if already polling, stop it
        $('#displaythis').empty(); // empty out the display 
        $("#dopolling").prop("value", "Start Polling"); // toggle the button text
    }
});

Now that we have created our trend list, we will start polling the data to create the live data stream. The $(“#dopolling”).click(function() checks to see if a trend list id exists. If it doesn’t, it displays an error message and exits the function. If an id does exist, the function then checks to see if the current trend list is a historical trend. Historical trends only return one data set, so we don’t want allow the user to turn polling on and off. If it is historical, we display an error message and exit the function. Next, we want to see if the user is trying to stop or start the polling by looking at whether or not the polling variable exists. If it doesn’t exist, we start the polling by calling the setInterval(gettrendlist, 3000). This will call the gettrendlist function every three seconds. Then we immediately call the gettrendlist function so we don’t have to wait three seconds for it to poll the first time.

In the gettrendlist function, we start by checking to see if our display table header has already been drawn, we only want to draw it the first time we poll. If it hasn’t been drawn, we draw it by creating a column header for Time Stamp and then looping through the currentlist.tags array and creating a column header for each tag item, using the tag property. Then we set the drawheader flag to false, so that we don’t draw it again. Next, we make our API call appending the currentlist.id to the url so the server knows which list we are looking for. It is a “GET” type , we pass our clientid and token in the header and there is no data needed. In the success function, the first thing we do is calculate the interval between points based on r.firsttime and r.lasttime and the number of data points returned. We then loop through the tags creating the table rows and and adding them to the table. We create the timestamp by adding our interval to the r.firsttime each time we go through. Then, we do some housekeeping and check to see if this is a historical trend. If it is, we stop the polling and clear it out because we only want to display data once for an historical trend. In the event of an error, we display the message.

If the user is trying to stop the polling we handle that on the other side of our if statement by clearing the polling. Then we clear the display and toggle the button.

Update Trend List

// click function to handle add Tank Level
$("#updatetrendlist").click(function(){
    if (!currentlist.id){  // if there is no trend list to add it to, exit
        displaymessage("Create a Trend List First", "update");
    return;
    }
    if(currentlist.history){ // if historical trend, exit, we are not logging Tank Level
        displaymessage("Not available for Historical Trend", "update");
        return;
    }  
    if(currentlist.tags.some(el => el.tag === "Tank Level.Value")){  // if the Tank Level already is included, exit
        return;
    }else{ // otherwise, add it to the tag array
        currentlist.tags.push({"tag": "Tank Level.Value"});
    }
    drawheader = true;  // we need to redraw the header, so reset the flag
    $("#tbtrend").empty();  // clear out the current display
    // api call to update the trend list            
    $.ajax({
        url: networkNode + "/OASREST/v2/trendlists",
        type: "PUT",
        contentType: "application/json; charset=utf-8", 
        crossDomain: true,
        dataType: "json",
        headers: {"clientid": clientid, "token": token},
        data: JSON.stringify(currentlist),
        success: function(r) {
            //polling = setInterval("getthetags()", 1000);
        },
        error: function (e) {
            displaymessage(e.status, "update");   // in case of error, display the error                 
        }
    });
});

When the user clicks the Add Tank Level button we need to update the trend list. In the $(“#updatetrendlist”).click(function(), the first thing we do is to check if we have a list to update. If we don’t we display an error message and exit the function. Then, we check to see if this is an historical trend, in which case we display an error message and exit the function. We are not logging historical data for the tank tag and, therefore, can’t display it. After that, we check to see if our list already has the Tank Level included. We only want it in the list once so we will exit the function if it is. If it is not, we use the array.push method to add the Tank Level to our object. Next, we set the drawheader flag back to true because we want the header be redrawn now that we have added a new tag. Then, our API call which is a “PUT”. We send the clientid and the token in the header and in the data parameter we format our currentlist object and send the server our updated list. In the success function, we resume polling. In the event of an error, we display the message.

Create Historical Trend

// click event to create the historical trend
    $("#createtrendhistory").click(function(){
    if(currentlist.tags.some(el => el.tag === "Tank Level.Value")){  // we don't want the Tank Level for this, if it exists, remove it
        currentlist.tags.pop();
    }
    clearvariables() // starting a new list, clear out old variables and display
    currentlist.history = true; // set the history to true
    currentlist.firsttime = "1620232331000";
    currentlist.lasttime = "1620232341000";
    $.ajax({
        url: networkNode + "/OASREST/v2/trendlists",
        type: "POST",
        contentType: "application/json; charset=utf-8", 
        crossDomain: true,
        dataType: "json",
        headers: {"clientid": clientid, "token": token},
        data: JSON.stringify(currentlist), // format and set the data for the call
        success: function(r) {
            console.log(r);
            currentlist.id = r.data.id; // set the current list id from the data returned
            $('#divtrendlistid').html(currentlist.id); // display the trend list id
            polling = setInterval(gettrendlist, 3000); // start the polling
            gettrendlist(); // get the data for the new list
        },
        error: function (e) {
            displaymessage(e.status, "history");  // in case of error, display the error                  
        }
    });
});

To create an Historical Trend list we use the same API call we do to create a real-time trend list but we add a few extra parameters. In the $(“#createtrendhistory”).click(function() we first check to see if the Tank Level is included on our currentlist. If it is, we remove it with array.pop(). We are not data logging this tag, so we can not show history for it, only real-time data. Next, we clear out our variables because most likely the user has already created a real-time trend list. Then, we set the history property of our currentlist object to false and add some date parameters. This is an historical trend so we need to give the server a date range, it is looking for firsttime and lasttime. These values are sent in in tick format, a numeric representation of the number of ticks since 1/1/1970. In JavaScript this can be retrieved from a Date object using the .GetTime() method. The call is a “POST” type and we send the clientid and the token in the header. In our success function, we set the currentlist.id from the id that is returned from the call and display it, start the polling and immediately call the gettrendlist() function gain so that we don’t have to wait the three seconds. In the event of an error, we display the message.

The Whole Ball-of-Wax

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
    <title>REST Example | Real Time Data</title>
    <link rel="stylesheet" href="client.css">
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script type="text/javascript">

        var networkNode = "http://www.opcweb.com:58725";
        var clientid =  ""; // holds the client id from authorization
        var token =  "";    // holds the token from authorization       
        var polling = null; // variable for setInterval function    
        var pumpval = null; // holds the last pump value
        var currentlist = {
            "tags":[    
                {"path": "Pump.Value"},
                {"path": "Ramp.Value"},
                {"path": "Sine.Value"}
            ]
        };

            // function to clear out variables for a reset
        function clearvariables(){
            delete currentlist.id;
            clearInterval(polling);
            polling = null;
            $('#diverror').empty();
            $('#divtaglistid').empty();
            $('#displaythis').empty();
            $("#createtaglist").prop("value", "Create Tag List");
            $("#dopolling").prop("value", "Start Polling");
        }
        
        // function to display error messages 
        function displaymessage(mess, fnc){
            clearInterval(polling); // in the event of an error we stop the polling
            switch(mess) {
                case 401:
                    $('#diverror').html("Authentication required.");
                    break;
                case 404:
                    if((fnc == "delete")||(fnc == "update")){
                        $('#diverror').html("Tag List not found.");
                    }else{
                        $('#diverror').html("404"); 
                    }
                    break;
                default:
                    $('#diverror').html(mess);
                }                       
        }
        
        // api call to get the latest tag values for the list
        function getthetags(){
            $.ajax({
                url: networkNode + "/OASREST/v2/taglists/" + currentlist.id,
                type: "GET",
                contentType: "application/json; charset=utf-8", 
                crossDomain: true,
                dataType: "json",
                headers: {"clientid": clientid, "token": token},
                success: function(r) {
                    console.log(r);
                    pumpval = r.tags[0].value; // hold onto the pump value so we can update it
                    $('#displaythis').empty(); // empty out old display
                    $.each(r.tags, function(key,tag) {
                        $('#displaythis').append(tag.path + ': ' + tag.value + "<br>");               
                    });              
                },
                error: function (e) {
                    displaymessage(e); // in case of error, display the error    
                }
            }); 
        }
        
        $(document).ready(function() {
            
            $("#doauth").click(function(){ // click funtion for authorization
                clearvariables(); // clear out the old variables, we are starting over   
                // api call for authorization
                $.ajax({
                    url: networkNode + "/OASREST/v2/authenticate",
                    type: "POST",
                    contentType: "application/json; charset=utf-8", 
                    crossDomain: true,
                    dataType: "json",
                    data: '{"username": "", "password": ""}',
                    success: function(r) {
                        clientid = r.data.clientid; // store the client id for later calls
                        token = r.data.token;  // store the token for later calls      
                        $('#divid').html(clientid); // display the client id
                        $('#divtoken').html(token); // display the token 
                    },
                    error: function (e) {
                        displaymessage(e.status, "auth"); // in case of error, display the error         
                    }
                });
            });
            
            // click function to create or delete tag list 
            $("#createtaglist").click(function(){
                // if there is no id, create one
                if (!currentlist.id){ 
                    // api call to create the tag list
                    $.ajax({
                        url: networkNode + "/OASREST/v2/taglists",
                        type: "POST",
                        contentType: "application/json; charset=utf-8", 
                        crossDomain: true,
                        dataType: "json",
                        headers: {"clientid": clientid, "token": token},
                        data: JSON.stringify(currentlist),
                        success: function(r) {
                            currentlist.id = r.data.id;                            
                            $('#divtaglistid').html(currentlist.id);
                            $('#diverror').empty();
                            $("#createtaglist").prop("value", "Delete Tag List"); // toggle button
                        },
                        error: function (e) {
                            displaymessage(e.status, "create"); // in case of error, display the error    
                        }
                    });
                // if there is an id, delete it
                }else{
                    clearInterval(polling); // stop the polling and clear out the variable                   
                    polling = null;
                    $("#dopolling").prop("value", "Start Polling"); // toggle polling button
                    // api call to delete the tag list
                    $.ajax({
                        url: networkNode + "/OASREST/v2/taglists/" + currentlist.id,
                        type: "DELETE",
                        contentType: "application/json; charset=utf-8", 
                        crossDomain: true,
                        dataType: "json",
                        headers: {"clientid": clientid, "token": token},
                        success: function(r) {
                            if(currentlist.tags.some(el => el.path === "Random.Value")){  // if the random tag has been added, remove it to reset to original list
                                currentlist.tags.pop(); 
                            }
                            delete currentlist.id; 
                            $('#divtaglistid').empty();  //empty the displays
                            $('#displaythis').empty();
                            $("#createtaglist").prop("value", "Create Tag List"); // toggle button
                        },
                        error: function (e) {
                            displaymessage(e.status, "delete"); // in case of error, display the error   
                        }
                    });
                }
            });

            // click event for adding random tag
            $("#updatetaglist").click(function(){
                if (currentlist.id == ""){ // if no list exists, exit
                    displaymessage("Tag List not found", "update");
                    return;
                }
                if(!currentlist.tags.some(el => el.path === "Random.Value")){  // check to see if it is already there, if it's not add it
                    currentlist.tags.push({"path": "Random.Value"});                
                    // api call to update the tag list
                    $.ajax({
                        url: networkNode + "/OASREST/v2/taglists",
                        type: "PUT",
                        contentType: "application/json; charset=utf-8", 
                        crossDomain: true,
                        dataType: "json",
                        headers: {"clientid": clientid, "token": token},
                        data: JSON.stringify(currentlist),
                        success: function(r) {
                        },
                        error: function (e) {
                            displaymessage(e.status, "update");  // in case of error, display the error                  
                        }
                    });
                }
            });           

            // click event to toggle the pump value
            $("#togglepump").click(function(){
                if (currentlist.id == ""){ // if no list exists, exit
                    displaymessage("Tag List not found", "toggle");
                    return;
                }
                var flagpump = false;
               if (pumpval == "False"){ // see what the current stored pump value is and flip it
                    flagpump = true;
                }
                // api call to update the pump value
                $.ajax({
                    url: networkNode + "/OASREST/v2/taglists/set",
                    type: "POST",
                    contentType: "application/json; charset=utf-8", 
                    crossDomain: true,
                    dataType: "json",
                    headers: {"clientid": clientid, "token": token},
                    data: '{"values":[{"path": "Pump.Value", "value": "' + flagpump + '"}]}',
                    success: function(r) {
                    },
                    error: function (e) {
                        displaymessage(e.status, "update");  // in case of error, display the error                          
                    }
                });
            });

            // click event to toggle polling
            $("#dopolling").click(function(){
                if (!currentlist.id){  // if no list exists, display message, exit
                    displaymessage("Tag List not found", "poll");
                    return;
                }
                if (polling == null){ // if polling exists, stop it
                    polling = setInterval("getthetags()", 1000);  // start the polling 
                    $("#dopolling").prop("value", "Stop Polling"); // toggle button
                } else { //if polling doesn't exist, start it
                    clearInterval(polling);  // stop the polling and clear the variable
                    polling = null;
                    $("#dopolling").prop("value", "Start Polling"); // toggle button
                }
            });

        });
</script>
</head>
    <body>
        <div class='main'>
            <input type='button' id='doauth' class='button' value='Authenticate'><input type='button' id='createtaglist' class='button' value='Create Tag List'><input type='button' id='dopolling' class='button' value='Start Polling'><input type='button' id='updatetaglist' class='button' value='Add Random Tag'><input type='button' id='togglepump' class='button' value='Toggle Pump'><br><br>
            <div class='outer'><div class='label'>Client ID:</div><div  id='divid' class='value'></div></div>
            <div class='outer'><div class='label'>Token:</div><div id='divtoken' class='value'></div></div>
            <div class='outer'><div class='label'>Tag List ID:</div><div id='divtaglistid' class='value'></div></div>         
            <div class='outer'><div class='label'>Message:</div><div  id='diverror' class='value'></div></div><br>
            <div id='displaythis'></div>
        </div>
    </body>
</html>

To download the source code for this tutorial, click here.