Simple Sensor Web Page Example (Web Page Template)

The Simple Sensor web page shows a more complete web page for an FT 6050 EVB NcMultiSensor application. You can modify this web page to support your device, or to learn how to create a web page. The FT 6050 EVB is an evaluation board that you can purchase and which can be used to test your web pages.

This web page is designed to be a web page template so you can quickly create a custom web page to access your devices. 

This section consists of the following:

Simple Sensor Overview

The Simple Sensor is made up of the following four files:

  • simplesensor.html  This file is web-page specific and contains the HTML elements used by the web page. Change the HTML elements to support your device datapoints. 
  • scripts/simplesensor.js  This web-page specific javascript can be modified for the HTML elements that you use in simplesensor.html file. 
  • scripts/simpledriver.js  This file can be used by multiple web pages and contains the low-level source code that communicates to the SmartServer.
  • css/simple.css – This file can be used by multiple web pages.

To start developing a simple web page for one or more datapoints, you need to change the simplesensor.html and the simplesensor.js files.




The simpledriver.js file supports input, span and drop down (select HTML), and image HTML elements that require data from one or multiple datapoints. You can use the built-in read/write functions or you can tie a function to an HTML when the value changes.

When a datapoint value is updated, the corresponding HTML elements are updated. For structured (multi-field), when any one of the fields are updated all HTML elements are updated even if the field used for this HTML element was not updated. When your web page writes to a datapoint, all HTML elements on the web page that use this datapoint are also updated in addition to sending the new value to the SmartServer.

This web page uses a datapoint array (g_dpList) to store a list of all datapoints. For each datapoint used in the simplesensor.html file, you need to add one entry in g_dpList. You also have to add one displayElement entry into the g_dpList for each HTML element that uses that datapoint. 

Sections of code that you should change for your custom web page are highlighted by "*** Custom Web page # ***".  

The source code below shows a subset of the HTML and javascript that is needed to support a web page.

Simple Sensor Source Code

Part of the source code that you need to modify:

SimpleSensor.html Source Code
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8"/>
	<title>Simple Sensor</title> <!-- *** Custom Web page #1  *** -->
	<link rel="stylesheet" type="text/css" href="css/simple.css">	<!-- app-wide styles -->
	<script src="scripts/simpledriver.js"></script> <!-- javaScript used by multiple Web pages  -->
	<script src="scripts/simplesensor.js"></script> <!-- Javascript - unique for this Web page -->
</head>
<body onload="init()"> 
<div><a class="logout" id="logout">Log out</a></div>
<br>
<!-- Begin *** Custom Web page #2  *** -->
<h1>Simple Sensor</h1>
LampIn: State <input id="Sensor1/Lamp/0/nviValue/state" > Value <input id="Sensor1/Lamp/0/nviValue/value" >
<button type="button" onclick="writeValue('Sensor1/Lamp/0/nviValue')">Write</button>
<br><br>
LampOut: State <span id="Sensor1/Lamp/0/nvoValueFb/state">...</span>Value <span id="Sensor1/Lamp/0/nvoValueFb/value">...</span>
<br><br>
Lux: <span id="Sensor1/LightSensor/0/nvoLuxLevel">...</span>
<!-- End *** Custom Web page #2  *** -->
</body>
</html>


Key functions in simplesensor.js:

simplesensor.js Source Code
function init1() {
			
	/*  **** Begin -- *** Custom Web page #4  *** -Change code below   *****   */
	g_sWebpageTag = "xyz_uitest=1";  // needs to be unique for each Web page, up to three additional tags get automatically 
									//created based on this namedepending on datapoint settings
	ivMax_Age = -1; // -1= on-demand polling disabled(Only, MODBUS, BACNET, IOX or Internal Devices)
					//  0=all datapoints use max_age (all devices are external LON devices), 
					//  1=Web page has External LON devices (and needs on on-demand polling) and NON-LON devices

	var dpObj;
	var displayObj;
			
	dpObj = new dpObject("Sensor1/Lamp/0/nviValue",false);
	dpObj = addDisplayElementToDpObject(dpObj, "Sensor1/Lamp/0/nviValue/state", "state", "rw", "input");
	dpObj = addDisplayElementToDpObject(dpObj, "Sensor1/Lamp/0/nviValue/value", "value", "rw", "input");
	g_dpList.push(dpObj);

	dpObj = new dpObject("Sensor1/Lamp/0/nvoValueFb",true);
	dpObj = addDisplayElementToDpObject(dpObj, "Sensor1/Lamp/0/nvoValueFb/state", "state", "r", "span");
	dpObj = addDisplayElementToDpObject(dpObj, "Sensor1/Lamp/0/nvoValueFb/value", "value", "r", "span");
	g_dpList.push(dpObj);

	dpObj = new dpObject("Sensor1/LightSensor/0/nvoLuxLevel",true);
	dpObj = addDisplayElementToDpObject(dpObj, "Sensor1/LightSensor/0/nvoLuxLevel", "", "r", "span");
	g_dpList.push(dpObj);

	/*  **** End -- *** Custom Web page #4  *** - Change code above    *****   */
}

function writeValue(dpName) {
			/*  **** Begin -- *** Custom Web page #5  *** -Change code below   *****   */			
	var valueField;
	var stateField;
	var value;

	valueField = document.getElementById(dpName + "/value").value;  // ONLY works if dpname/fieldname value are used as element id
	stateField = document.getElementById(dpName + "/state").value; // ONLY works if dpname/fieldname value are used as element id
	value = "{\"state\":" + stateField + ",\"value\":" + valueField + "}"
	if(validateData(1,"SNVT_switch","",value)) // SmartServer doesn't validate values, so your Web page needs to
	{
		requestWriteData(dpName,value);
	}
	else 
	{ // bad value so change back to old value
		
	}
	/*  **** End -- *** Custom Web page #5  *** - Change code above    *****   */
}

function validateData(mode,snvtType, field, valueStr) {
	// check write data as SmartServer doesn't check
	// mode: 0=respond back true=valid, false=invalid, 1=same as 0 plus show alert
	// field: ""=entire DP
	/*  **** Begin -- *** Custom Web page #6  *** -Change code below   *****   */
			
	/*  **** End -- *** Custom Web page #6  *** - Change code above    *****   */
	return bResult;
}


Source Code Description

How it Works

  1. This example supports HTML input, button, span, drop-down (select), and image elements for reading from and writing to devices. 

    A single datapoint can be used more than once on a single web page. You can also specify a function to process a datapoint value update for a given HTML element, such as an image swapper (image changes based on datapoint value). Additionally, you can specify functions that work with multiple datapoints (jsFunction). In this case, the specify function gets called when  one or more datapoint values have changed. 

    An example of HTML elements is shown below.  

    Example: An HTML span is used to show a datapoint value.

    <span id="id1"></span>

    Example: An HTML input can be used to show and write a datapoint value. It is not uncommon for one datapoint (an input datapoint) to be used to write to a device, and a second datapoint (output datapoint know as a feedback) to be used for reading. See "Adding an Image Swapper" for an example on using two datapoints to support a clickable image swapper.
     
    <input id="id2" >

    The span and input ids must be unique and can be anything. Including the datapoint path in the id makes it easier to identity which html elements you are using.

    To make it easier to identify HTML elements you can create a HTML id based on the datapoint path.  If a single datapoint is only used once, then you can make the id the same as the datapoint path as shown below. If the HTML element is showing a datapoint field, you may want to include the field name in the id as id="<datapoint path>/<field name>". 

    Example: A web page that shows four HTML elements for the same structured datapoint (for example a LON SNVT_switch) containing a "state" and "value", requires four unique HTML element ids:

    datapoint: Sensor1/Lamp/0/nviValue

    <span id="Sensor1/Lamp/0/nviValue"></span>
    <span id="Sensor1/Lamp/0/nviValue_2"></span>
    <span id="Sensor1/Lamp/0/nviValue/state"></span>
    <span id="Sensor1/Lamp/0/nviValue/value"></span>


    The last two <span> above demonstrate that the spans are used to show the datapoint field values instead of the entire datapoint value.

    Important HTML Information

    HTML element ids must be unique.
  2. This example uses a background timer to periodically poll specified datapoints at a calculated poll rate (that is, does a periodic GET request). GET request will get the latest information stored in the SmartServer. 

    There are two types of poll rates that important for web page development.  One poll rate is how often you send out a GET request to get datapoint values from the SmartServer. When poll rate is mention it is the GET poll rate. The second poll rate is related to the SmartServer getting live data from the end device and is setup in the CMS Datapoint Properties widget.  

    Depending on the channel protocol that the device is using (i.e., external LON devices, Internal LON devices, Modbus, BACnet, or other protocols), the GET request will return live data or just stored data. For some device protocols, to get the up-to-date value, you may need to set up datapoint monitoring for  periodic polling or event-driven updates for each device (configured in the dla file or CMS Datapoint Properties widget). 

    Device Protocols:
        a. Internal LON device: datapoint changes are automatically stored in the SmartServer database.
            GET request returns the latest values.  
        b. External LON device: datapoint changes are not automatically stored in the SmartServer database. 
            You need to change the monitoring rate, make event driven or use GET request max_age to see live updates.  
        c. MODBUS: datapoint changes are not automatically stored in the SmartServer database. 
            You need to change the monitoring rate to see live updates.  
        d. BACnet: datapoint changes are not automatically stored in the SmartServer database. 
            You need to change the monitoring rate to see live updates.  
        d. IOX: datapoint changes are automatically stored in the SmartServer database. 
            GET request returns the latest values.    

    max_age cannot be used for non-external LON devices. Doing so can use up the CPU bandwidth. When datapoints are added, you will need to specify if they use max_age or not.

    For external LON devices you may want to have some datapoints use max_age and some without max_age, since using max_age uses up more of the LonTalk channel bandwidth.

    This simpledriver.js uses datapoint tags to reduce the number of GET requests it makes to the SmartServer. Datapoint tags allow a single request to get values from multiple datapoints.

    This driver supports four different types of datapoint GET request. Each GET request type uses a different datapoint tag based on a unique web page tag you create with g_sWebpageTag.

    For example:
          g_sWebpageTag = "xyz_mytag=1";

    Supported GET request types:
        a. normal periodic polling (no max_age)
        b. normal one-time (no max_age): reads the value only once
        c. max_age periodic polling: Only for external LON devices
        d. max_age one-time: does two request with max_age, the first to request the on-demand poll and then 10 seconds later to read the new update.

    By default, simpledriver.js will automatically determine the how often GET polling occurs and the best max_age values to use based on the GET request type used by each datapoint. This is done to provide the best performance of web page update times as well as to reduce traffic on the LonTalk channels. You can manually set the GET request poll rate and max_age, but it is recommend to do so only if you want to make the times longer, not shorter.    

    For example, to disable automatic calculation for poll and max_age rates, and then poll rate to 60 seconds and max_age to 30:
          g_bCalculatePollRate = false; //default is true
          g_timerInterval = 60000;  // in msec
          g_iMax_Age = 30;  // -1= no DPs using max age, 0= all DPs use max age (max age auto calculated), 1= (Default) some DPs use max age, some DPs do not (max_age auto calculated),  >1 = use this value for max_age 

    In most cases you will want to use the poll and for max_rates to be automatically calculated.

  3. Writing to a datapoint requires you to create a write function.

    1. All datapoint writes are for the full datapoint value (that is, you cannot write directly to an individual field).
      1. If writing to a single field, you need to do a read modified write. 
    2. Part of writing data requires you to validate the format of the data that you send to the SmartServer.
    3. The SmartServer will validate whether the write request is correct, but it will not validate whether the payload is correct for a specific datapoint type.
    4. Your code must validate any data that you write.
    5. A Feedback delay is necessary when writing to a datapoint in order to stop datapoint value flickering in the HTML element (datapoint value changes back and forth to the old and new value, and then eventually goes to the new value).
      1. This can happen after writing to a datapoint in which the next GET request has the stale data (the old data) instead of the new value. This is especially true when datapoint values are not automatically updated in the SmartServer (e.g., external LON devices).
      2. The simplesensor.js code has a 10 second feedback delay to reduce web page flickering.

  4. This web page uses a g_dpList array to store a list of all datapoints. The g_dpList is built up in the scripts/simpledriver.js file. For each datapoint used in the simplesensor.html file, an entry must be added in the g_dpList in the simplesensor.js init1() function.

    The following is information only – a short cut for creating the g_dpList is shown in the examples below.

    If you have a datapoint that can be changed in two places on your web page, then you need to add a datapoint Oject to g_dpList with two displayElement objects.

    datapointNamePath = <device name>/<block name>/<block index>/<datapoint name> 

    <input id="id1">
    <input id="id2">

    g_dpList[iIndex] = {}; // You need to add the datapoint to the g_dpList array 
    g_dpList[iIndex].dpName = datapointNamePath; /
    g_dpList[iIndex].displayElements = []; // list of HTML element using this DP, create one entry per HTML element (e.g., span, input)

    For the first HTML element used for this datapoint, a displayElement object must be added to the specific g_dpList[iIndex].

    g_dpList[iIndex].displayElements[0] = {};
    g_dpList[iIndex].displayElements[0].displayId = "id1";
    g_dpList[iIndex].displayElements[0].field = ""; // ""=entire value, not equal "" means field value
    g_dpList[iIndex].displayElements[0].type = "rw"; //rw = read/write, ro=readonce, row=readonce-write, r=readonly, w=write only
    g_dpList[iIndex].displayElements[0].displayType = "input"//html element types input, span
    g_dpList[iIndex].displayElements[0].function = null; // displayType = "function": function name, display

    To add a second HTML element, a second displayElement object needs to be added to this datapoint.

    g_dpList[iIndex].displayElements[1] = {};
    g_dpList[iIndex].displayElements[1].displayId = "id2";
    g_dpList[iIndex].displayElements[1].field = "state"; // ""=entire value, not equal "" means field value
    g_dpList[iIndex].displayElements[1].type = "rw"; //rw = read/write, ro=readonce, row=readonce-write, r=readonly, w=write only
    g_dpList[iIndex].displayElements[1].displayType = "input"//html element types input, span
    g_dpList[iIndex].displayElements[1].function = null; // displayType = "function": function name, display


    Shortcut to create the same g_dpList[] object.

    dpObj = new dpObject(datapointNamePath, true);    // create a datapoint object for dpList
    dpObj = addDisplayElementToDpObject(dpObj, "id1", "", "rw", "input", null); // Add first HTML element
    dpObj = addDisplayElementToDpObject(dpObj, "id2", "state", "rw", "input", null); // Add second HTML element

    g_dpList.push(dpObj);   // adds the datapoint object to g_dpList

  5. Adding datapoints to the g_dpList using dpObject() 
    1. All datapoints used for reading must be included in the g_dpList array using dpObject.
    2. If you have a write datapoint that needs the current value for read modified writes then you can add that datapoint to the g_dpList. 
      1. This is sometimes necessary for structured datapoints in which you want to only change a single datapoint field. 
    3. If an HTML element uses one datapoint for reading (output datapoint) and another for writing (input datapoint) then the reading datapoint is added to g_dpList, and the writing datapoint is used in your custom write function.

          var dpObj = new dpObject(datapointNamePath, bUseMaxAge);   
       // create a datapoint object for dpList
          //
          // Add one or Display element useing addDisplayElementToDpObject
          //
          g_dpList.push(dpObj);   // adds the datapoint object to g_dpList

      datapointNamePath: <device name>/<block name>/<block index>/<datapoint name>
      bUseMaxAge: true=use max age for live updates, false= use polling or event-driven for live updates

      e.g., 
                  dpObj = new dpObject("Sensor1/Lamp/0/nvoValueFb",true);    // create a datapoint object for dpList
       
  6. Adding HTML elements for a specific datapoint using addDisplayElementToDpObject().

    addDisplayElementToDpObject() allows you to tie an HTML element (also called display element) to a specfic datapoint so that you can see datapoint updates in a custom web page. 

    addDisplayElementToDpObject(dpObject, displayId, fieldName, readWriteType, displayType, extraParameter)

    dpObject  The datapoint dpObj that you are adding the display element.
    displayId  This is the HTML element id. For example: <input id="id1>, displayId="id1".
    fieldName  "" for Scalar datapoints. For structured datapoint "" means entire datapoint value and non "" points to datapoint field. For example: "state" means datapoint value.state.
    readWriteType  //rw = read/write, ro=readonce, row=readonce-write, r=readonly, w=write only.
    displayType – HTML element type: "input", "span", "dropdown", "dropdown_text", function. 
    extraParameter  Null when not used. Depends on displayType, displayType="function" then extraParameter is function Name. displayType="span" then extraParameter is number of decimal places. 

  7. HTML displayElements displayTypes 
    All HTML elements need to be added using to a datapoint list using addDisplayElementToDpObject except for jsFunctions.

    e.g., 
                dpObj = addDisplayElementToDpObject(dpObj, "id1", "state", "rw", "input", null); 

          Supported Display Element Types (displayTypes):
    1. input  allows you to see and change a datapoint value
    2. span  allows you to see a datapoint value
    3. dropdown_text and dropdown_number
      1. If the drop-down is for the entire datapoint value (scalars only), then the drop-down value is automatically sent when the value is changed.
        1. If drop-down is for a field in a structured datapoint, then you need to use a button and your own click event to send the datapoint update.
      2. A dropdown of "---" means no change. This is needed so that you do not mistakenly touch the drop-down and send a value. If you mistakenly touch the drop-down, simply change the value to "---" and nothing will be sent.
        <option value="_nochange_">—
    4. function
      1. This is a callback function tied to a HTML element, which is called when a datapoint value is updated.
    5. jsFunction
      1. A callback function that is called when the value of at least one datapoint in a list of datapoints changes.
      2. Added using addJavascriptFunctionTojsFunctionObject
  8. Process for adding a datapoint and any display elements to the g_dpList array.
    1. For each datapoint you need for your web page you need to create a datapoint Object called dpObj.
      Format:
          dpObj = new dpObject(datapointPath, bUseMaxAge);    // create a datapoint object for dpList
      Example:
         dpObj = new dpObject("Sensor1/Lamp/0/nvoValueFb",true);    // create a datapoint object for dpList

    2. For each HTML element that uses the datapoint you need to add a displayElement object to the dpObj.displayElements array.
      Format:
          dpObj = addDisplayElementToDpObject(dpObj, displayElementId, field, "r", "span", null); // Add an HTML element
      Example:
          dpObj = addDisplayElementToDpObject(dpObj, "Sensor1/Lamp/0/nvoValueFb/state", "state", "r", "span", null); // Add an HTML element

    3. Finally, you need to add the dpObj to the g_dpList array.
          g_dpList.push(dpObj);   // adds the datapoint object

  9. Example: Single datapoint with a single HTML element in the web page.

    This example shows a <span> for a "state"  field in "Sensor1/Lamp/0/nvoValueFb" using max_age in the GET request.

    <span id="Sensor1/Lamp/0/nvoValueFb/state">

    var dpObj = new dpObject("Sensor1/Lamp/0/nvoValueFb",true);    // create a datapoint object for dpList
    dpObj = addDisplayElementToDpObject(dpObj, "Sensor1/Lamp/0/nvoValueFb/state", "state", "r", "span", null); // Add an HTML element
    g_dpList.push(dpObj);   // adds the datapoint object


  10. Example: Single datapoint with two HTML elements in the web page.  

    This example shows two HTML elements for the same scalar (single field) datapoint in "Controller1/Lamp/0/nviValue" not using max_age in the GET request.

    <input id="Controller1/Lamp/0/nviValue">
    <span id="Controller1/Lamp/0/nviValue">

    dpObj = new dpObject("Controller1/Lamp/0/nviValue",false);  
    dpObj = addDisplayElementToDpObject(dpObj, "Controller1/Lamp/0/nviValue", "", "rw", "input", null);
    dpObj = addDisplayElementToDpObject(dpObj, "Controller1/Lamp/0/nviValue_1", "", "r", "span", null);  //even though we want to use the same datapoint for both HTML elements the span id has to be different
    g_dpList.push(dpObj);


  11. Example: Single datapoint with single element in the web page that shows a precission of 1 decimal places

    <span id="Controller1/Lamp/0/nvoValue/">

    dpObj = new dpObject("Controller1/Lamp/0/nvoValue",false);  
    dpObj = addDisplayElementToDpObject(dpObj, "Controller1/Lamp/0/nvoValue", "", "r", "span", 1);
    g_dpList.push(dpObj);

  12. Example: Single datapoint with single element in the web page in which a function is used to process the datapoint.

    <span id="Controller1/Lamp/0/nvoValue/">

    dpObj = new dpObject("Controller1/Lamp/0/nvoValue",false);  
    dpObj = addDisplayElementToDpObject(dpObj, "Controller1/Lamp/0/nvoValue", "", "r", function, myFunction);
    g_dpList.push(dpObj);

    myFunction(displayId,dpName,fieldName, value) { //process value update }

  13. When writing to a datapoint value:
    1. You can use the built functionality to write to a datapoint value, or add your own write functions.
    2. When writing to structured datapoints (multi-field datapoints), you may want to use a button function to indicate that you changed all the fields.
      1. HTML element displayType="dropdown_text" can be used to automatically send the datapoint value after selecting an option.

      2. As it is easy for users to select the <select> dropdown, a "_nochange_" option is included, which means ignore this entry.
        <option value="_nochange_">---</option>

        Dropdown source code
        <select id= "opcDev.1/uiTestTarget2/0/nviLampSw_2/state">
        	<option value="_nochange_">---</option>
        	<option value="SW_SET_ON">SW_SET_ON</option>
        	<option value="SW_SET_OFF">SW_SET_OFF</option>
        	<option value="SW_RECALL_SCENE">SW_RECALL_SCENE</option>
        </select>
        
        var dpObj = new dpObject("opcDev.1/uiTestTarget2/0/nviHvacMode",false);
        dpObj = addDisplayElementToDpObject(dpObj, "opcDev.1/uiTestTarget2/0/nviHvacMode", "" , "rw", "dropdown_text"); 
        g_dpList.push(dpObj);


    3. When sending a write request for a structured datapoint you need to send the entire datapoint value.
      1. That is, you cannot just write to a datapoint field.
      2. There are two ways to change datapoint fields.
        1. Do a read modified write to change the value. In this case, you use the last read value, change the field that you want, and then send the complete datapoint value to the SmartServer.
        2. Same as above, but have a default value with default field value.
           
  14. Adding an image swapper (using functions on datapoint changes for specific HTML elements).

    1. You can tie a function to an HTML element instead of using the default function when a datapoint changes.

      1. Image swapper allows you to use two or more images based on datapoint value.

    2. Image swapper example  two or more images that change based on the value of the datapoint.

    3. Example:   

      1. Add two image <img> elements with the first <img> being used as the default image and used for the image swapper.
        1. The first <img> will include the HTML element id for the image swapper an onclick event and the size of the image.
        2. The second <img> is not displayed, but is used to pre-load the image so you will see the image instantly.

          <img id="Controller1/Lamp/0/nvoValueFb/state_2"  src="images/switchTradGreenOn.gif" style="height:25px;width:25px">
          <img  src="images/switchTradOff.gif" style="display:none">
           
      2. Add a structured datapoint with a state field that can have a value of  0 or 1, to init1()
          
      3. Add the lampImageSwapper(displayId, dpName, value) function source code.
        1. As this is a clickable image swapper, you will only swap between two images.

      Image Swapper Source Code
      <img id="Controller1/Lamp/0/nvoValueFb/state_2" src="images/switchTradGreenOn.gif" style="height:25px;width:25px">
      <img  src="images/switchTradOff.gif" style="display:none">
      
      
      dpObj = new dpObject("Controller1/Lamp/1/nvoValueFb",true);
      dpObj = addDisplayElementToDpObject(dpObj, "Controller1/Lamp/1/nvoValueFb/state_2", "state", "r", "function", lampImageSwapper);
      g_dpList.push(dpObj);
      
      //******************************************************************
      // lampImageSwapper
      //		displayId - this is the HTML element id
      //		dpName - this is the datapoint path name e.g., device1/block/0/nvoValue
      //		value - value used for image swapper.
      //******************************************************************
      function lampImageSwapper(displayId, dpName, value) {
      	var src;
      	try 
      	{
      		if((displayId == null) || (dpName == null) ||(value ==null))
      			return;
      		if((displayId == "") || (dpName == "") ||(value == ""))
      			return;
      			
      		if(typeof value === "string"){
      			value = Number(value);
      		}
      		var src =  "images/switchTradOff.gif";
      		if(value === 1)
      			src = "images/switchTradGreenOn.gif";
      		if(!document.getElementById(displayId).src.endsWith(src))
      			document.getElementById(displayId).src = src;
      		}
      		catch(e){}
      	}
      }
  15. Clickable image swapper example (using two datapoints for a single HTML element).
    This example is similar to the example above except that clickable image swappers typically only swap between two images, and you need to add an onclick event to the <img>.

    1. For some HTML elements, like image swappers, you need to use a custom function to process the datapoint value.
    2. This examples shows how two datapoints are used for a specific HTML <img>.
      1. In this case, one datapoint is used for the current value (a output datapoint), which you add to the g_dpList.
      2. A second datapoint is used for writing (or updating) a datapoint value. In this case, you use a write function that you create to change the value with input datapoint.
        1. This datapoint is not added to g_dpList. It only is used in the write function.
    3. Clickable image swappers typically only swap back and forth between two images.
    4. You need to add an onclick event to the HTML element and a javascript function to process the onclick event.

    5. For example:

      1. Add two image <img> elements with the first <img> being used as the default image and used for the image swapper.
        1. The first <img> will include the HTML element id for the image swapper, an onclick event, and the size of the image.
        2. The second <img> is not displayed, but is used to pre-load the image so you will see the image instantly.

          <img id="Controller1/Lamp/0/nvoValueFb/state_2" onclick="imageStateClicked(this,'Controller1/Lamp/0/nviValue')" src="switchTradGreenOn.gif" style="height:25px;width:25px">
          <img  src="switchTradOff.gif" style="display:none">
           
      2. Add a structured datapoint with a state field that can have a value of  0 or 1, to init1().  
      3. Add the lampImageSwapper(displayId, dpName, value) function source code.
        1. As this is a clickable image swappe,r you will only swap between two images.
      4. Add the limageStateClicked(displayObject, writeDpName) function source code.
        1. This function gets called whenever the "state" field is changed. 
        2. The image gets swapped and the new value is sent to the end device.

      Clickable Image Swapper Source Code
      <img id="Controller1/Lamp/0/nvoValueFb/state_2" onclick="imageStateClicked(this,'Controller1/Lamp/0/nviValue')" src="images/switchTradGreenOn.gif" style="height:25px;width:25px">
      <img  src="images/switchTradOff.gif" style="display:none">
      
      
      dpObj = new dpObject("Controller1/Lamp/1/nvoValueFb",true);
      dpObj = addDisplayElementToDpObject(dpObj, "Controller1/Lamp/1/nvoValueFb/state_2", "state", "r", "function", lampImageSwapper);
      g_dpList.push(dpObj);
      
      //******************************************************************
      // lampImageSwapper
      //		displayId - this is the HTML element id
      //		dpName - this is the datapoint path name e.g., device1/block/0/nvoValue
      //		value - value used for image swapper.
      //******************************************************************
      function lampImageSwapper(displayId, dpName, value) {
      	var src;
      	try 
      	{
      		if((displayId == null) || (dpName == null) ||(value ==null))
      			return;
      		if((displayId == "") || (dpName == "") ||(value == ""))
      			return;
      			
      		if(typeof value === "string"){
      			value = Number(value);
      		}
      		var src =  "images/switchTradOff.gif";
      		if(value === 1)
      			src = "images/switchTradGreenOn.gif";
      		if(!document.getElementById(displayId).src.endsWith(src))
      			document.getElementById(displayId).src = src;
      		}
      		catch(e){}
      	}
      }
      
      //******************************************************************
      // imageStateClicked()
      // 		SNVT_switch value has two fields: state and value; this function only changes state
      // 		displayObject - this is usually using an output datapoint, though it can be an input datapoint
      // 		writeDpName - this is an input datapoint;
      //******************************************************************
      function imageStateClicked(displayObject, writeDpName) {
      	var displayId = displayObject.id;
      	var iIndex = -1;//DP index in g_dpList
      	var value;
      	var json;
      	var dpInfo;
      	try {
      		dpInfo = currentDpValueById(displayId);  // output
      		if(dpInfo != null){
      			if((dpInfo.dpName != "") && (dpInfo.value != "") && (dpInfo.value != "")) {
      				value = dpInfo.value;
      				iIndex = dpInfo.index;
      				json = JSON.parse(value);
      					// toggle state between 0 and 1
      				if(document.getElementById(displayId).src.endsWith("switchTradGreenOn.gif")) {
      					json.state = 0;
      					document.getElementById(displayId).src = "images/switchTradOff.gif";
      				}
      				else {
      					json.state = 1;
      					document.getElementById(displayId).src = "images/switchTradGreenOn.gif";
      				}
      				value = JSON.stringify(json);
      				requestWriteData(writeDpName, value); // updates all other HTML elements using this datapoint and writes the value to the end device
      			}
      		}
      	}
      	catch(e) {}
      }
  16. Accessing multiple datapoints with a Function (jsFunction).

    1. jsFunctions allows you tie a multi-datapoint function to a single HTML element, which allows you to sum or the average the values of multiple datapoints.

    2. jsFunctions are processed when the value of any of the selected datapoints change.
      1. For structured datapoints, the jsFunction will be called when any field is updated. It is up to the jsFunction to check if the specific field is updated. 
    3. Determine which datapoints you want to use.

    4. You can specify a parameter list (parameters) for the jsFunction so that your Function has all the information it needs.
    5. You can specify an HTML element id (displayId), which ties tell the function of the HMTL element to process.

    6. Examples for summing two datapoint values together:  

      1. Add an HTML that shows the sum of two power values:

        <span id="sum">
         
      2. Add a function called "sum" with two structured datapoints to init1():
        1. sum the value field.
            
      3. Add the sumValueField(parameters, displayId, dpList) function source code:
        1. This function will add the value field of  two structured datapoints, which has a two fields (state and value).

      Adding jsFunction Source code
      <span id="sum">
      
      jsFunctionDpList = [];
      dpObj = new dpObject("Controller1/switch/0/nvoValueFb",true);
      jsFunctionDpList.push(dpObj);
      dpObj = new dpObject("Controller1/switch/1/nvoValueFb",true);
      jsFunctionDpList.push(dpObj);
      addJavascriptFunctionTojsFunctionObject(null, "sum", jsFunctionDpList, sumValueField);
      
      
      
      //******************************************************************
      //  sumValueField() 
      //
      //		This function sums the value field of one or more datapoints
      //
      //		This jsfunction is called the value or field value of at least one datapoint changes.
      // 
      // 		parameter: user defined paramter list used by your Function - can be a string, number, object or array
      // 		displayId: used to associate a html element (like span) to the jsFunction.
      // 		dpList: lists datapoint Pathnames
      //******************************************************************
      function sum(parameters, displayId, dpList) {
      
      			
      	var i;
      	var json;
      	var bValid = true;
      	var sum = 0;
      	if(dpList == null)
      		return;
      	try {	// This example only cares about values
      		for(i = 0; i < dpList.length; i ++) 
      		{
      			try {
      				valueStr = dpList[i].value; // full data point value
      				if(valueStr == "") {
      					bValid = false;
      					break;
      				}
      				else {
      					json = JSON.parse(valueStr);
      					value = json.value;  // SNVT_switch value field
      					sum += value;
      				}
      			} catch (err) {}
      		}
      		if(bValid) {
      			document.getElementById(displayId).innerHTML = sum;
      		}
      	} catch (err) {}
      }
      
      

Creating a Custom Web Page

  1. Determine which datapoints you want to use, and use the CMS Datapoints widget to determine the datapoint path.
    <device name>/<block name>/<block index>/<datapoint name>

  2. Copy simplesensor.html and rename it to another name (for example, test.html).

  3. Copy scripts/simplesensor.js and rename it (for example, test.js).

  4. Change test.html to have the look and feel you want. You may need a different write function for each button.

  5. Change test.js:  
    1. Change init1()
      1. Change the g_sWebpageTag to a unique web page tag.
      2. Change g_iMax_Age to specify if you are using on-demand polling (max_age).
        • In most cases you will set g_iMax_Age=1 to support both on-demand and non-on-demand periodic polling.
      3. Build up the g_dpList for all the HTML elements in the test.html web page.
        • Specify if the datapoint uses max age when you add the datapoint to g_dpList.
                dpObj = new dpObject(datapoint_path, bUseMaxAge);
    2. Modify or add write functions.
    3. Modify or add validate function for any write data.
    4. Add any jsFunctions to process multiple datapoints for a single HTML element.

Common Problems with Creating a Custom Web Page with simpledriver.js

  1. A datapoint name shows up more than once in g_dpList (a single datapoint can only show up once).
    1. Use the web browser debugger to look at the g_dpList[] datapoint list. Go to simpledriver.js and put a breakpoint in the timer function.
  2. An HTML element id shows up more than once in g_dpList. Each HTML element id in g_dpList need to be unique. 
    1. Use the web browser debugger to look at the g_dpList[].displayElements HMTL id list.
      <device name>/<block name>/<block index>/<datapoint name>

Modify simpledriver.js

Modify simpledriver.js to the new functionality, such as allowing input element writes to happen when pressing an enter key, instead of just by clicking a write button.