Custom Web Page Tutorial

This Custom Web Page Tutorial shows you the step-by-step process of creating a custom web page. You will learn how to customize a Simple Sensor web page to allow you to read and write datapoints for your device, an FT 6050 EVB (refer to the Setting up an Internal Application Program Example Network section for more information about this evaluation board) or a SmartServer internal app. 

This tutorial only shows how to work with datapoint tags and writing using the Normal priority (17, which is the default). That is, it does not show working with localized values, presets, or override priorities, which is a new features starting with SmartServer version 2.8.

The tutorial web page source code files (SimpleSensor) are written for the FT 6050 EVB with the NcMultiSensor application (EVB default image) with a single device called "Sensor1". You will be modifying the source code to use other datapoints on the EVB, or you can use datapoints from one of your devices or a SmartServer internal app. 

A SmartServer internal app called "UI Test Tester" is provided for those who do not have any devices but still want to learn how to create a custom web page. When using this internal app, you will use two of the datapoints for your initial custom web page. You can also add other datapoints on the "UI Test Tester" app.

The Simple Sensor web page source files are used as a starting point for developing a custom web page. See the Simple Sensor Web page for more information about how this example web page works.


This section consists of the following:

Requirements

To get started, you will need the following:

  1. SmartServer IoT 2.6 or higher
  2. SFTP program, such as WinSCP or CoreFTP
  3. Terminal emulation program, such as Putty or TeraTerm
  4. Web-page editor, such as Visual Studio Code, Windows Code Writer, a full web-page editor, or any other text editor
  5. USB-to-MicroB USB cable to connect to the SmartServer console port
  6. A device (your device or a FT 6050 EVB) or use the provided uiTestTester internal app

Getting Started Instructions

  1. Download the following files to your computer by clicking the following link SmartServer-IoT Example Repo
    1. Simple Sensor Web Page  which you will modify (SmartServer Github Repository under iot/smarterver-iot/web-pages).

    2. uiTestTester Internal App  optional SmartServer internal app you can use for this tutorial.
      1. UI Test Target App.zip  contains the internal app and documentation (SmartServer Github Repository under iot/smarterver-iot/apps/Example Support Files).
      2. uiTestTarget.dtp  provide the Resource files, XIF and DLA files (SmartServer Github Repository under iot/smarterver-iot/apps/Example Support Files).
      3. CustomWebPageTutorialSolutions  provides the solutions for this tutorial (SmartServer Github Repository under iot/smarterver-iot/apps/web-pages).
        1. uiTest1  solution for uiTestTarget for the datapoints used in the steps below.
        2. uiTest  A complete web page for UI Test Target Tester which uses different types of structured datapoints.

  2. Read and understand how the "Simple Sensor Web Page Example (Web Page Template)" works before continuing.
    1. This example source code shows a simple web page for a FT 6050 EVB that can be used to create a custom web page.
    2. You will need to understand how to add datapoints and HTML objects.
    3. Read and understand the Simple Sensor web page documentation as it will help when developing your custom web page to this web page template.
      Simple Sensor Web Page Example (Web Page Template)
    4. Review the test section below (step 14) before starting as it will help you troubleshoot your code.

  3. Add at least one device to the SmartServer.
    You will be changing or replacing the existing HTML and simplesensor.js source except if you are using the NcMultiSensor. In this case, you will add to the existing source code.
    1. You can use an FT 6050 EVB with NcMultiSensor application.
      1. The provided source code will work without changes if the device name is Sensor1. 

        Create this web page:



    2. Use one of your devices.
      1. It is best not to use a device that is currently being used on a production site.
    3. Use your SmartServer internal app.
      1. Use the UI Test Target Internal App  used if you do not have any devices, or just need something to experiment with.

        Create this web page:



      2. You will be creating a web page for a small subset of the UI Test Target Internal App datapoints.
      3. Follow the instructions in the "Summary of Steps" below describing how to add the internal app.

        Summary of Steps on how to add the UI Test Target App
        1. Use the default Standalone(DMM) mode 
        2. Use CMS Widget Device Types to Import the UITestTarget.dtp file
            - Wait until you see "Device type uiTestTarget2 is loaded message" in the CMS Web page (about 1 minute)
        3. Use WinSCP to create the following directory
        	/var/apollo/data/apps/ui-test 
        4. Unzip the "UI Test Target App.zip" file
        5. SFTP uiTestTarget2.js, package.json, and package-lock.json to ui-test directory
        6. ssh (don't use SmartServer console) and type 
        	cd /var/apollo/data/apps/ui-test
        7. Type following npm install command (The SmartServer must have Internet connection)
        	 npm install 
        8. Start the App: choose one
            a. Run temporarily while ssh window open: from ssh console type 
        		- To run with minimal logging
        			node uiTestTarget2.js 
        		- To run with verbose logging
                     node uiTestTarget2.js 1
                - node console shows datapoint changes and is used for troubleshooting
        		- To exit close ssh session or use CTRL-C
        		- To restart run the command again
            b. Run Permanently (runs after reboots)
                i. Use WinSCP to copy the uiTestTarget.conf to 
        			/etc/supervisor/conf.d
                ii. Reboot the SmartServer
                iii. To see logs or errors run one of the following
                     - ssh and type 
        				cd /var/apollo/data/apps/ui-test
                     - to see Internal App log 
        				"tail -f stdout-uiTest.log" or "tail -f stderr-uiTest.log"
        
        
        
  4. Determine which datapoints you want to monitor and control.
    1. Find your two datapoints and write down the path.
      1. For UI Test Target Internal App, find the datapoint path for "nviLampSw" and "nvoLampFb".
      2. For FT 6050 NcMultiSensor, find the datapoint path for second Lamp input and output.
      3. For your device, see if you have an input and output datapoint that you can control.
    2. Go to the CMS Datapoints widget and find the datapoints for your web page. For SmartServer 3.3 and prior, the Datapoints widget is called the Datapoint Browser widget.
    3. You will need to use at least one input and one output datapoint. Input datapoints are read/write and output datapoints are read-only.
      You can still go through this example if you only have one datapoint. 
      1. The datapoint path uses the following format:
                  Datapoint path = <Device path>/<Block name>/<Block Index>/<Datapoint name>
        1. For the screen capture above, the datapoint path is 
          ssi_1u60852 1.Subsystem 1.Device1/Switch/1/nvoValue
        2. In an IMM network, the device path has the following format:
          1. device path = <subsystem path>/<device name>
          2. <subsystem path> can have multiple subsystems separated by "."
          3. In the example above, the <subsystem path> = "ssi_1u60852 1.Subsystem 1"
          4. For example: datapoint path = "ssi_1u60852 1.Subsystem 1/Device1/Switch/1/nvoValue"
        3. For DMM, the device path = <device name>
          1. For example: datapoint path = "Device1/Switch/1/nvoValue" 
      2. The datapoint value shows "state:0,value:0" indicating this is a structured datapoint.
        1. A scalar (non-structured datapoint) would show up as a single value like "0".
        2. In most cases your web page will show a scalar value or a field of a structured datapoint.
          1. For a structured SNVT_switch with two fields you would typically show each field separately, instead of the complete field.

  5. Setup datapoint monitoring with a DLA file or with the CMS Datapoint Properties widget.
    1. The SmartServer only shows datapoints updates if you have:
      1. IMM  polling enabled.
      2. DMM  polling or event-driven updates enabled.
    2. For the purpose of testing web pages when using polled datapoints, you will want to set the polled rate to 1 or 2 seconds.
      1. Ensure that the logging rate is not less than 1 minute.
      2. Keep total poll rate (all datapoints, and polling on LonTalk, events, Modbus) need to be 5 polls per second or less.
      3. For UI Test Target  the DTP file that you imported earlier already included the poll rates. This step can be skipped.

  6. Change the default Login Prompt.
    1. Create login.html: SFTP to /var/apollo/www/user and rename login1.html to login.html
    2. Create LoginLog.png: SFTP to /var/apollo/www/images and rename LoginLog1.png to LoginLog.png

  7. Unzip the Simple Demo Web Page and SFTP the Simple Sensor Web files to /var/apollo/www/user/test.
    1. Create a test directory so you have /var/apollo/www/user/test.
    2. Unzip Simple Demo Web Page.
    3. SFTP the following to /var/apollo/www/user/test.
      1. simplesensor.html
      2. css and scripts folders

  8. Only for FT6050 with NcMultiSensor App – try testing the provided web page.
    1. Enter the following URL into your web browser.
      1.  /var/apollo/www/user/test
    2. You should see changes in the lux sensor when you put your hand over the EVB lux sensor.
    3. Change the SW in value and click the write button. 
    4. If you have monitoring enabled, you will see changes within 5 seconds.

  9. Edit simplesensor.html for your datapoints.
    1. Change the title.
    2. Add two HTML elements to your HTML body. Note the "id" must be unique for each HTML element.
      1. It is helpful if your HTML element "id" uses some portion of the datapoint path.
        1. If the HTML element shows the entire datapoint value, use the datapoint path as the "id".
          For example: 
          1. "Device1/Switch/0/nviValueFb"
        2. If the HTML element is used to show one of the datapoint fields then you may want to add the field name to the end of the datapoint. 
          For example: 
          1. "Device1/Switch/0/nviValueFb/value"
        3. If two HTML elements for the same datapoint field are used, then you need to make sure they are unique.
          For example:
          1. "Device1/Switch/0/nviValueFb/value"
          2. "Device1/Switch/0/nviValueFb/value1"
      2. For this tutorial you will add HTML elements for input and output datapoints, and a button to write the input datapoint to the end device.

        1. Sample HTML elements for scalar datapoints:

          Html elements for Structured Datapoints
          Input: <input id="id1" > 
          <button type="button" onclick="writeValue()">Write</button>
          <br><br>
          Temperature <span id="id2">...</span>
          
          
          
        2. Sample HTML elements for structured datapoints:

          Html elements for Structured Datapoints
          SwitchIn: State <input id="id1" > Value <input id="id2" >
          <button type="button" onclick="writeValue()">Write</button>
          <br><br>
          SwitchOut: State <span id="id3">...</span>Value <span id="id4">...</span>
          
          
          
      3. Add the elements for each example.
        1. For UI Test Target Internal App, you will be showing two fields for each SNVT_switch datapoint:
          1. Write data using an input element. For example, change the "id" below to match your datapoints.
            <input id="Device1/Switch/0/nviValueFb/value">
          2. Add a read datapoint using a span element.

            <span id="Device1/Switch/0/nvoValue/value">

        2. For an FT 6050 EVB:
          1. Provide two input HTML elements for the following datapoint so you can write data to an external device. 
            Each field should be placed in its own <input> field.
            Lamp/1/nviValueFb
          2. Add a button to write the value that you changed.
          3. Provide two input HTML elements for the following datapoint so you can display data stored in the external device. 
            Each field should be placed in its own <span> field.
            Lamp/1/nvoValue
        3. For your device:
          1. Write data using an input element. For example, change the "id" below to match your datapoints.
            <input id="id1">
          2. Add a read datapoint using a span element.

            <span id="id2">

    3. Add a button which will be used to change the value on the end device.
    4. Remove all other HTML elements if you are not using an FT 6050 EVB NcMultiSensor device.

  10. Edit simplesensor.js for your datapoint to read and write datapoint values.
    1. Each web page needs a unique web page tag that is used for reading datapoint values.
      1. Change the value of g_sWebpageTag to a new tag using the following format: "<company 3 character initials>_<Webpagename>=1"
      2. For example g_sWebpageTage="gle_controller=1"
    2. You will need to change the init1() function to add the datapoints and HTML elements to g_dpList array and any write functions you may need.
    3. You will need to add the following HTML elements:
      1. For structured datapoints, you should add a separate input and span for each field. 
      2. For scalar datapoints, you only need to use a single input and single span for each datapoint. 
    4. Sample code to be added:

      Structured datapoints:

      simplesensor.js changes
      <input id="id1" > <input id="id2" >
      <span id="id3"><span id="id4">
      
      function init1() {
      	g_iMax_age = 1;
      
      	dpObj = new dpObject(myInputDatapoint,false);  
      	dpObj = addDisplayElementToDpObject(dpObj, "id1", "state", "rw", "input", null);
      	dpObj = addDisplayElementToDpObject(dpObj, "id2", "value", "rw", "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);
      
      	dpObj = new dpObject(myOututDatapoint,false);  
      	dpObj = addDisplayElementToDpObject(dpObj, "id3", "state", "r", "span", null);
      	dpObj = addDisplayElementToDpObject(dpObj, "id4", "value", "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);
      
      }


      Scalar datapoints:

      simplesensor.js for a Scalar value
      <input id="id1" >
      <span id="id2">
      function init1() {
      	g_iMax_age = 1;
      
      	dpObj = new dpObject(myInputDatapoint,false);  
      	dpObj = addDisplayElementToDpObject(dpObj, "id1", "", "rw", "input", null);
      	g_dpList.push(dpObj);
      
      	dpObj = new dpObject(myOututDatapoint,false);  
      	dpObj = addDisplayElementToDpObject(dpObj, "id2", "", "r", "span", null);
      	g_dpList.push(dpObj);
      
      }
      1. For UI Test Target Internal App only:
        You will be reading and writing structured datapoints. Both the "nviLampSw" and "nvoLampFb" are SNVT_switch, which means that the datapoint value has "state" and "value" fields.
        For example: nviLampSw.value = {"state":1,"value":75}
        You need to add the following:
        1. Two input HTML elements for nviLampSw1, one for state and one for value.  

        2. Two span HTML elements for nvoLampFb.
      2. For all other devices or apps: add g_dpList object for each datapoint and a displayElement for the HTML elements on your web page.
        This is for the FT 6050 EVB NcMultiSensor, so for other devices or internal apps, change the code to use your datapoint paths.
        You need to add the following:
        1. Keep the current init1() and HTML code but at new elements for Lamp/1.
      3. For your device, find input or output datapoints that you can use for the web page.

    5. Change the dpName and id to match the HTML elements you added in the previous step.
    6. Change g_sWebpageTag to a unique name.
    7. Remove all other g_dpList items if you are not using an FT 6050 EVB NcMultiSensor device.
      1. For FT 6050, you will use the existing source code and add to it.
    8. Change the write function as needed.
    9. Change or remove the validation function.

  11. Add an image swapper.
    1. Find a datapoint for your image swapper and write down the path. For this tutorial you will use  an output datapoint.
      1. For UI Test Target App, find the datapoint path for "nvoLampFb".
      2. For FT 6050 NcMultiSensor,find the datapoint path for "nvoLampFb".
      3. For your device, see if you have a input and output datapoint that you can control.
        1. You may want to use your own image files.
        2. You will need to change the lampImageSwapper function to work with your datapoints.
    2. Use the following source code in your files (you will need to change the datapoint names and ids).

      Image Swapper Source Code
      <img id="id1" src="switchTradGreenOn.gif" style="height:25px;width:25px">
      <img  src="switchTradOff.gif" style="display:none">
       
       
      dpObj = new dpObject("inputdatapoint",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.  This is the full datapoint value or a datapoint field value
      //******************************************************************
      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 =  "switchTradOff.gif";
              if(value === 1)
                  src = "switchTradGreenOn.gif";
              if(!document.getElementById(displayId).src.endsWith(src))
                  document.getElementById(displayId).src = src;
              }
              catch(e){}
          }
      }
  12. Make an image swapper clickable.
    1. This example takes your existing image swapper and makes it clickable.
      1. You will be adding an onclick event to one of your <img> elements and which calls a function that will write a new value to an input datapoint.
    2. Find an input datapoint that corresponds to the output datapoint you used in the previous section.
      1. For UI Test Target Internal App, you will use "nvoLampFb" as the output datapoint and "nviLamp" as the input value.
      2. For FT 6050 NcMultiSensor, find the datapoint path for second Lamp input and output.
      3. For your device, see if you have a input datapoint that you can control for the image swapper you created in the previous step.
    3. You will need to add an onclick event to the <img> element that you assigned an id to.
      1. Add an onclick function to your <img> with an id, change id and inputdatapoint to match your HTML element and datapoint name.
        onclick="imageStateClicked(this,'Controller1/Lamp/0/nviValue')"
        <img id="id1" onclick="imageStateClicked(this,'inputdatapoint')" src="switchTradGreenOn.gif" style="height:25px;width:25px">
    4. Add a function for the onclick event and use an input function to change the value
      1. imageStateClicked() is an example of an onclick event that you can use.
    5. Use the following source code in your files (you will need to change the datapoint names and ids).

      Image Swapper onClick Function Source Code
      //******************************************************************
      // imageStateClicked()
      // 		SNVT_switch value has two fields: state and value; this function only changes state
      //
      // 		displayObject - HMTL element object, 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 = "switchTradOff.gif";
      				}
      				else {
      					json.state = 1;
      					document.getElementById(displayId).src = "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) {}
      }
  13. Add a jsFunction that gets called when one or more datapoint values change (e.g.; summing datapoints).
    1. Find two datapoints in which you can sum the values.
      1. For UI Test Target Internal App, you will use "nvoLampFb" and "nviLamp".
      2. For FT 6050 NcMultiSensor, you will sum the nvoValueFb of both block 1 and block 2.
      3. For your device, determine if you have two datapoints that you can sum.
        1. The sumValueField() function below sums the value field for a SNVT Switch.
        2. To sum the value of the two scalar values make the following change:

          1. Change sumValueField() the jsFunction source code below by changing the "// change this code if using scalars datapoints" section of the code.

            Source Code for Scalar values
            				else {  
            						// change this code if using scalars datapoints
            					sum += = JSON.parse(valueStr);  // summing scalar values
            				}
    2. Use the following source code in your files (you will need to change the datapoint names and ids).

      jsFunction Source Code
      <span id="sum">
      
      jsFunctionDpList = [];
      dpObj = new dpObject("datapoint1",true);
      jsFunctionDpList.push(dpObj);
      dpObj = new dpObject("datapoint2",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 whenever 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 {  
      						// change this code if using scalars datapoints
      					json = JSON.parse(valueStr);
      					value = json.value;  // SNVT_switch value field
      					sum += value;
      				}
      			} catch (err) {}
      		}
      		if(bValid) {
      			document.getElementById(displayId).innerHTML = sum;
      		}
      	} catch (err) {}
      }
  14. Testing.
    1. Make sure web development tool does not show any formatting issues like missing "}".
    2. Access the new web page for example /user/apollo/www/user/test/test.html.
    3. Refresh the web page using Ctrl-F5 after making any changes to your web browser.
      1. You may need to delete the temporary internet files, exit the web browser, or even reboot the computer to see some changes.
    4. Check the web browser console to see if any errors. The console will show what line in the file is wrong.



    5. If the web page does not show datapoint values within 15 seconds (should be a lot faster), then there is probably an issue with your web page.
    6. Use the web browser debugger to debug your web page.
      1. Firefox - Click Menu > Web Developer > Debugger



        1. Look at "Network" to see network traffic.
        2. Debugger to step through your code.
        3. Console to see if there are HTML element issues.
      2. Chrome  Click Menu > More Tools > Developer Tools.
    7. Make sure all datapoints in the g_dpList are unique, are datapoint pathname, and that all displayElement HTML ids are unique.
    8. If everything is working correctly, you should be able to write to a datapoint and see the output change within 10 seconds.
    9. See Web Page Troubleshooting for additional troubleshooting tips.

  15. Change the SmartServer home.
    1. Rename home1.html to home.html.
      1. /var/apollo/www/user/home.html

  16. Change the SmartServer home page to point to your test/simplesensor.html.
    1. Edit home.html and change the custom web page button to go to test/simplesensor.html.