Procházet zdrojové kódy

Added test arduino code for HDSv2

TC pushbot 5 před 4 roky
rodič
revize
cf33e8b0df

+ 3 - 1
examples/HomeDynamic2/Base/Base.ino

@@ -114,7 +114,9 @@ void handle_endpoints() {
     \"Name\": \"Hello World\",\
     \"RelPath\":\"hello\",\
     \"Desc\":\"Print out Hello World in Serial\",\
-    \"Type\":\"none\"\
+    \"Type\":\"none\",\
+    \"AllowRead\":false,\
+    \"AllowWrite\":true\
   }]"); 
 }
 

+ 200 - 0
examples/HomeDynamic2/Examples/Examples.ino

@@ -0,0 +1,200 @@
+/*
+ * Home Dynamic System v2
+ * Designed by tobychui
+ * 
+ * This is an example of making use of all avaible endpoints definations
+ * 
+ */
+
+#include <ESP8266WiFi.h>        // Include the Wi-Fi library
+#include <ESP8266WiFiMulti.h>   // Include the Wi-Fi-Multi library
+#include <ESP8266mDNS.h>        // Include the mDNS library
+#include <ESP8266WebServer.h>   // Include the WebServer library
+
+//Change the properties of the IoT device
+const String DeviceName = "HDsv2-Test"; //The name of this IoT device
+const int ListeningPort = 12110;        //The port where this IoT device listen
+
+//Library Objects
+ESP8266WiFiMulti wifiMulti;     // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
+ESP8266WebServer server(ListeningPort);    //Create an Web Server on the listening port
+
+//Change the WiFi Settings
+void WiFiConfig(){
+  wifiMulti.addAP("Toby Room Automation", "homedynamicsystem"); 
+  //Add more WiFi AP here if nessary
+  //wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
+  //wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
+}
+
+//Inject zeroconf attr into the MDNS respond (For scanning by ArozOS)
+void MDNSDynamicServiceTxtCallback(const MDNSResponder::hMDNSService p_hService) {
+    //Define the domain of the HDSv2 devices
+    MDNS.addDynamicServiceTxt(p_hService, "domain","hds.arozos.com");
+    MDNS.addDynamicServiceTxt(p_hService, "protocol","hdsv2");
+
+    //Define the OEM written values
+    MDNS.addDynamicServiceTxt(p_hService, "uuid",getMacAddress());
+    MDNS.addDynamicServiceTxt(p_hService, "model","Test Unit");
+    MDNS.addDynamicServiceTxt(p_hService, "vendor","HomeDynamic Project");
+    MDNS.addDynamicServiceTxt(p_hService, "version_minor","0.00");
+    MDNS.addDynamicServiceTxt(p_hService, "version_build","1");
+}
+  
+
+void hostProbeResult(String p_pcDomainName, bool p_bProbeResult) {
+  MDNS.setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallback);
+}
+
+void setup() {
+  //Use 115200 baudrate on serial monitor if you want to see what is happening to the device
+  Serial.begin(115200);
+  delay(10);
+  Serial.println('\n');
+
+  //Start WiFi Conenction Routines
+  WiFiConfig();
+  
+  Serial.println("Connecting ...");
+  while (wifiMulti.run() != WL_CONNECTED) {
+    delay(500);
+    Serial.print('.');
+  }
+  Serial.println('\n');
+  Serial.print("Connected to ");
+  Serial.println(WiFi.SSID());
+  Serial.print("IP address:\t");
+  Serial.println(WiFi.localIP());
+  
+  //Startup MDNS Responder
+  MDNS.setHostProbeResultCallback(hostProbeResult);
+  
+  if (!MDNS.begin(DeviceName)) {             // Start the mDNS responder for esp8266.local
+    Serial.println("Error setting up MDNS responder!");
+  }
+
+  //Advertise the port that you are using
+  MDNS.addService("http", "tcp", ListeningPort);
+  Serial.println("mDNS responder started");
+
+  //Startup the Web Server Endpoints
+  delay(100);
+  server.on("/", handle_index);
+  server.on("/status", handle_status);
+  server.on("/eps", handle_endpoints);
+  server.on("/string", handle_string);
+  server.on("/integer", handle_integer);
+  server.on("/float", handle_float);
+  server.on("/bool", handle_bool);
+  server.on("/output", handle_output);
+  
+  server.begin();
+  Serial.println("HTTP server started");
+  Serial.print("Listening on port: ");
+  Serial.println(ListeningPort);
+}
+
+//Handlers for Web Server
+void handle_index() {
+  server.send(200, "text/html", "index"); 
+}
+
+void handle_string() {
+  String value = server.arg("value");
+  Serial.println(value);
+  //Use value (String) to do something
+  server.send(200, "text/html", "OK"); 
+}
+
+void handle_integer() {
+  String value = server.arg("value");
+  int number = value.toInt();
+  Serial.println(number);
+  //Use number (int) to do something
+  server.send(200, "text/html", "OK"); 
+}
+
+void handle_float() {
+  String value = server.arg("value");
+  int floatNumber = value.toFloat();
+  Serial.println(floatNumber);
+  //Use floatNumber to do something else
+  server.send(200, "text/html", "OK"); 
+}
+
+void handle_bool() {
+  String value = server.arg("value");
+  bool result = false;
+  if (value == "true"){
+    result = true;
+    Serial.println("True");
+  }else{
+    Serial.println("False");
+  }
+
+  //Use result to do something else
+  server.send(200, "text/html", "OK"); 
+}
+
+void handle_output() {
+  Serial.println("Hello World");
+  server.send(200, "text/html", "OK"); 
+}
+
+
+void handle_status() {
+  server.send(200, "application/json", "{\
+  \"String Input\":\"default value\",\
+  \"Integer Input\":1234,\
+  \"Float Input\":123.4,\
+  \"Boolean Input\":true\
+}"); 
+}
+
+void handle_endpoints() {
+  server.send(200, "application/json", "[\
+  {\
+    \"Name\": \"String Input\",\
+    \"RelPath\":\"string\",\
+    \"Desc\":\"Print out your input string to serial\",\
+    \"Type\":\"string\",\
+    \"Regex\":\"/[A-Za-z0-9]/g\"\
+  },\
+  {\
+    \"Name\": \"Integer Input\",\
+    \"RelPath\":\"integer\",\
+    \"Desc\":\"Print out your input integer to serial\",\
+    \"Type\":\"integer\",\
+  \"Min\":0,\
+  \"Max\":10\
+  },\
+  {\
+    \"Name\": \"Float Input\",\
+    \"RelPath\":\"float\",\
+    \"Desc\":\"Print out your input float to serial\",\
+    \"Type\":\"float\",\
+    \"Min\":0,\
+    \"Max\":10,\
+    \"Steps\":0.1\
+  },\
+  {\
+    \"Name\": \"Boolean Input\",\
+    \"RelPath\":\"bool\",\
+    \"Desc\":\"Print out your input string to \",\
+    \"Type\":\"bool\"\
+  },\
+  {\
+    \"Name\": \"Print Hello World\",\
+    \"RelPath\":\"output\",\
+    \"Desc\":\"Reply your request with Hello World\",\
+    \"Type\":\"none\"\
+  }\
+]"); 
+}
+
+
+//Main Loop
+void loop() { 
+   server.handleClient();
+   MDNS.update();
+ }

+ 16 - 0
examples/HomeDynamic2/Examples/helper.ino

@@ -0,0 +1,16 @@
+//Get MAC Address of the ESP8266, require WiFi
+String MACString;
+const char* getMacAddress(){
+  unsigned char mac[6];
+  WiFi.macAddress(mac);
+  MACString = "";
+  for (int i = 0; i < 6; ++i) {
+    MACString += String(mac[i], 16);
+    if (i < 5){
+      MACString += '-';
+    }
+  }
+  
+  const char* _result = MACString.c_str();
+  return _result;
+}

+ 44 - 2
subservice/WsTTY/README.md

@@ -1,5 +1,47 @@
 # WsTTY
 
 WebSocket Terminal Module for arozos system
-If you are using Windows as your host, do not use this subservice.
-Use the build in WsShell Instead.
+
+Access your OS terminal from ArozOS Web Desktop Interface
+
+### Installation
+
+Requires
+
+- Go 1.14 or above
+- Windows 7 or above / Debian 10 (Buster) or above
+
+```
+#Assume the arozos is installed under ~/arozos
+cd ~/arozos/subservice
+git clone https://github.com/aroz-online/WsTTY
+cd ./WsTTY
+./build.sh
+
+#Setup permission, optional but recommended
+sudo chmod 755 -R ./
+```
+
+### Gotty
+
+This project includes binary compiled from Gotty from https://github.com/yudai/gotty
+
+The license of the compiled binary is included in ./gotty/LICENSE. Those binary are necessary for WsTTY to operate normally under Linux environment
+
+### Wsshell
+
+WsTTY will execute Wsshell under Windows for compatibility mode. This might works a bit different from Gotty. Please use with your own risk.
+
+### Screenshots
+
+WsTTY running under Windows (powered by Wsshell)
+
+![](./img/win.png)
+
+WsTTY running under Linux (powered by Gotty)
+
+![](./img/linux.png)
+
+### License
+
+MIT

binární
subservice/WsTTY/img/linux.png


binární
subservice/WsTTY/img/win.png


+ 297 - 208
web/SystemAO/iot/hub/index.html

@@ -46,239 +46,328 @@
         #sideMenu{
             height: calc(100% - 85px);
         }
+		.primary.button{
+			background-color: #4287f5 !important;
+		}
         body{
             height:100%;
             background:rgba(247,247,247,0.95);
         }
     </style>
 </head>
+
+
 <body>
-    <div class="ts right sidebar overlapped vertical menu">
-        	<div class="item">
-        		<div class="ts header">
-        			ArozOS IoT Hub
-        			<div class="sub header">Universal IoT Controller</div>
-        		</div>
-        	</div>
-        	<a class="selectable item" onClick="loadDevList();hideSideMenu();">
-                <i class="refresh icon"></i> Refresh List
-            </a>
-        	<a class="selectable item" onClick="scanDevices();hideSideMenu();">
-                <i class="search icon"></i> Scan Devices
-            </a>
-			
-        	<div class="bottom item">
-                CopyRight ArozOS Project 2021
-            </div>
-    </div>
+	<div class="ts right sidebar overlapped vertical menu">
+	   <div class="item">
+		  <div class="ts header">
+			 ArozOS IoT Hub
+			 <div class="sub header">Universal IoT Controller</div>
+		  </div>
+	   </div>
+	   <a class="selectable item" onClick="loadDevList();hideSideMenu();">
+	   <i class="refresh icon"></i> Refresh List
+	   </a>
+	   <a class="selectable item" onClick="scanDevices();hideSideMenu();">
+	   <i class="search icon"></i> Scan Devices
+	   </a>
+	   <div class="bottom item">
+		  CopyRight ArozOS Project 2021
+	   </div>
+	</div>
+	<div class="pusher">
+	   <div class="ts menu">
+		  <a class="item noborder" href="index.html"><img class="ts ultrasmall circular image" src="img/main_icon.png"> IoT Hub</a>
+		  <a class="right item" onClick="toggleSideMenu();"><i class="content icon"></i></a>
+	   </div>
+	   <div id="devList" class="ts container">
+	   </div>
+	   <br><br><br>
+	</div>
 
-    <div class="pusher">
-        <div class="ts menu">
-            <a class="item noborder" href="index.html"><img class="ts ultrasmall circular image" src="img/main_icon.png"> IoT Hub</a>
-            <a class="right item" onClick="toggleSideMenu();"><i class="content icon"></i></a>
-        </div>
-        <div id="devList" class="ts container">
+	<!-- Show more information about thsi device-->
+	<div id="moreInfoInterface" class="ts active dimmer" style="display:none;">
+	   <div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#moreInfoInterface").fadeOut("fast");'>
+	   </div>
+	   <div id="informationItnerface" class="ts segment mainUI" style="height:80%;width:95%;overflow-y:auto;">
+		  <div class="ts header">
+			 Device Properties
+		  </div>
+		  <br>
+		  <div class="ts horizontal form">
+			 <div class="field">
+				<label>Device UUID</label>
+				<input id="duid" type="text" readonly="true">
+			 </div>
+			 <div class="field">
+				<label>IP Address</label>
+				<input id="ipaddr" type="text"  readonly="true">
+			 </div>
+			 <div class="field">
+				<label>Communication Port</label>
+				<input id="comport" type="text"  readonly="true">
+			 </div>
+			 <div class="field">
+				<label>Manufacturer</label>
+				<input id="manufacturer" type="text"  readonly="true">
+			 </div>
+			 <div class="field">
+				<label>Version</label>
+				<input id="version" type="text"  readonly="true">
+			 </div>
+		  </div>
+		  <br>
+		  <button class="ts primary button"  onClick='$("#moreInfoInterface").fadeOut("fast");'>Close</button>
+		  <br><br>
+	   </div>
+	</div>
 
-        </div>
-        <br><br><br>
-    </div>
-    
-            <div id="moreInfoInterface" class="ts active dimmer" style="display:none;">
-        	<div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#moreInfoInterface").fadeOut("fast");'>
-        	
-        	</div>
-        	<div id="informationItnerface" class="ts segment mainUI" style="height:80%;width:95%;overflow-y:auto;">
-    		<div class="ts header">
-    			Device Properties
-    		</div><br>
-    		<div class="ts horizontal form">
-    			<div class="field">
-    				<label>Device UUID</label>
-    				<input id="duid" type="text" readonly="true">
-    			</div>
-    			<div class="field">
-    				<label>Last Seen IP Address</label>
-    				<input id="lastseen" type="text"  readonly="true">
-    			</div>
-    			<div class="field">
-    				<label>Device Driver Identifier</label>
-    				<input id="ddi" type="text"  readonly="true">
-    			</div>
-    			<div class="field">
-    				<label>Device Information</label>
-    				<input id="dinfo" type="text"  readonly="true">
-    			</div>
-    			<div class="field">
-    				<label>Driver Found</label>
-    				<input id="driverfound" type="text"  readonly="true">
-    			</div>
-    		</div>
-    		<br>
-    		<button class="ts primary button"  onClick='$("#moreInfoInterface").fadeOut("fast");'>Close</button>
-    		<br><br>
-    	</div>
-    </div>
-    
-    
-    <div id="actionInterface" class="ts active dimmer" style="display:none;">
-    	<div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#actionInterface").fadeOut("fast");'>
-    	
-    	</div>
-    	<div id="actionMainUI" class="ts segment mainUI" style="height:80%;width:95%;">
-    		<iframe id="controlUI" src="" width="500px" height="800px"> </iframe>
-    	</div>
-    </div>
-    
-    <div id="nickNameSelector" class="ts active dimmer" style="display:none;">
-    	<div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#nickNameSelector").fadeOut("fast");'>
-    	
-    	</div>
-    	<div id="nicknameSelectorUI" class="ts segment mainUI" style="height:80%;width:95%;overflow-y:auto;">
-    		<div class="ts header">
-    			<div class="content">
-    				Nickname Settings
-    				<div class="sub header">Please select an UUID from below for changing its nickname.</div>
-    			</div>
-    		</div>
-    		<div class="ts container">
-    			<div id="nicknameChangeList" class="ts list">
-    				
-    			</div>
-    		</div>
-    	</div>
-    </div>
-    
-    <div id="manualDevConfig" class="ts active dimmer" style="display:none;">
-    	<div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#manualDevConfig").fadeOut("fast");'>
-    	
-    	</div>
-    	<div id="manualDevConfigUI" class="ts segment mainUI" style="height:80%;width:95%;">
-    		<div class="ts header">
-    			<div class="content">
-    				Manual Device Configuration
-    				<div class="sub header">Add devices that runs other protocol to the system</div>
-    			</div>
-    		</div>
-    		<div class="ts container">
-    		    <button class="ts primary tiny button" onClick="addDevViaIP();"><i class="add icon"></i>Add device via IP</button>
-    		    <button class="ts tiny button" onClick="openFolderForDev();"><i class="folder icon"></i>Open device folder</button>
-    		    <p>Current list of Non-HDS Devices</p>
-    		    <div id="customDevList" class="ts ordered list">
-                    <div class="item">Loading</div>
-                </div>
-    		</div>
-    	</div>
-    </div>
-     <div id="loadingMask" class="ts active dimmer" style="display:none;">
-        <div class="ts text loader">Loading</div>
-    </div>
-<script>
-var currentlyViewingDevices = "";
-var uselocal = false; //Use Local as command sender or use Host as command sender
-var username = $("#data_session_username").text().trim();
-//ao_module Float Window functions
-ao_module_setWindowTitle("IoT Hub");
-ao_module_setWindowSize(465,730,true);
-if (!ao_module_virtualDesktop){
-    $("body").css("background-color","white");
-}
+	<!-- Action can be done on this device -->
+	<div id="actioninterface" class="ts active dimmer" style="display:none;">
+		<div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#actioninterface").fadeOut("fast");'>
+		</div>
+		<div id="informationItnerface" class="ts segment mainUI" style="height:80%;width:95%;overflow-y:auto;">
+		   <div class="ts header">
+			  Device Actions
+		   </div>
+		   <br>
+		   <div class="ts horizontal form" id="actionForm">
 
-//Initiate the page content
-loadDevList();
+			</div>
+			<br>
+			<button class="ts primary button"  onClick='$("#actioninterface").fadeOut("fast");'>Close</button>
+			<br><br>
+		</div>
+	</div>
 
-    
-function inputbox(message, placeholder = ""){
-    var input = prompt(message, placeholder);
-    if (input != null) {
-      return input;
-    }else{
-      return false;
-    }
-}
+	<div id="loadingMask" class="ts active dimmer" style="display:none;">
+	   <div class="ts text loader">Scanning in Progress</div>
+	</div> 
+	<script>
+		var currentlyViewingDevices = "";
+		var uselocal = false; //Use Local as command sender or use Host as command sender
+		var username = $("#data_session_username").text().trim();
+		//ao_module Float Window functions
+		ao_module_setWindowTitle("IoT Hub");
+		ao_module_setWindowSize(465,730,true);
+		if (!ao_module_virtualDesktop){
+			$("body").css("background-color","white");
+		}
 
+		//Initiate the page content
+		loadDevList();
 
-function scanDevices(){
-    
-}
+			
+		function inputbox(message, placeholder = ""){
+			var input = prompt(message, placeholder);
+			if (input != null) {
+			return input;
+			}else{
+			return false;
+			}
+		}
 
 
-function hideSideMenu(){
-	ts('.right.sidebar').sidebar('hide');
-}
+		function scanDevices(){
+			$("#loadingMask").show();
+			$.get("../../../system/iot/scan", function(data){
+				loadDevList();
+				$("#loadingMask").hide();
+			});
+		}
 
-function showMore(object){
-	var device = $(object).parent().parent();
-	var duid = device.attr("uuid");
-	var lastseen = device.attr("devip");
-	var ddi = device.attr("classtype");
-	var dinfo = device.attr("classname");
-	var dfound = device.attr("driverfound");
-	if (duid === undefined){
-		duid = "Unknown";
-		$("#setNicknameButton").hide();
-	}else{
-		$("#setNicknameButton").show();
-	}
-	if (dfound === undefined){
-		dfound = "Offline";
-	}
-	$("#duid").val(duid);
-	$("#lastseen").val(lastseen);
-	$("#ddi").val(ddi);
-	$("#dinfo").val(dinfo);
-	$("#driverfound").val(dfound);
-	currentlyViewingDevices = duid;
-	$("#moreInfoInterface").fadeIn('fast');
-}
 
+		function hideSideMenu(){
+			ts('.right.sidebar').sidebar('hide');
+		}
 
+		function showMore(object){
+			var device = $(object).parent().parent();
+			var duid = device.attr("uuid");
+			var lastseen = device.attr("devip");
+			var deviceData = device.attr("devicedata");
+			deviceData = JSON.parse(decodeURIComponent(deviceData))
+			console.log(deviceData);
+			$("#duid").val(duid);
+			$("#ipaddr").val(lastseen);
+			$("#comport").val(deviceData.Port);
+			$("#manufacturer").val(deviceData.Manufacturer);
+			$("#version").val(deviceData.Version);
+			currentlyViewingDevices = duid;
+			$("#moreInfoInterface").fadeIn('fast');
+		}
 
-function loadDevList(){
-	$("#devList").html("");
-	$.get("../../../system/iot/list", function(data){
-		 if (data.error !== undefined){
-			 alert(data.error);
-		 }else{
-			data.forEach(device => {
-				deviceData = encodeURIComponent(JSON.stringify(device));
-				$("#devList").append(`<div class="ts segment HDSDev" devicedata="${deviceData}" uuid="${device.DeviceUUID}" devIp="${device.IPAddr}" port="${device.Port}" location="local">
-					<div class="ts grid">
-						<div class="four wide column"><img class="ts tiny devIcon image" src="img/system/loading.gif"></div>
-						<div class="twelve wide column">
-							<div class="ts container">
-								<div class="ts header">
-									<span class="devHeader">${device.Name}</span>
-									<div class="sub devProperty header">${device.Model}</div>
+		function loadDevList(){
+			$("#devList").html("");
+			$.get("../../../system/iot/list", function(data){
+				if (data.error !== undefined){
+					alert(data.error);
+				}else{
+					data.forEach(device => {
+						deviceData = encodeURIComponent(JSON.stringify(device));
+						$("#devList").append(`<div class="ts segment HDSDev" devicedata="${deviceData}" uuid="${device.DeviceUUID}" devIp="${device.IPAddr}" port="${device.Port}" location="local">
+							<div class="ts grid">
+								<div class="four wide column"><img class="ts tiny devIcon image" src="img/system/loading.gif"></div>
+								<div class="twelve wide column">
+									<div class="ts container">
+										<div class="ts header">
+											<span class="devHeader">${device.Name}</span>
+											<div class="sub devProperty header">${device.Model}</div>
+										</div>
+									</div>
 								</div>
 							</div>
-						</div>
-					</div>
-					<div class="controlBtn infoMount">
-						<button class="ts icon button" onClick="showMore(this);"><i class="notice icon"></i></button>
-						<button class="ts primary icon button" onClick="action(this);"><i class="external icon"></i></button>
-					</div>
-				</div>`);
+							<div class="controlBtn infoMount">
+								<button class="ts icon button" onClick="showMore(this);"><i class="notice icon"></i></button>
+								<button class="ts primary icon button" onClick="action(this);"><i class="options icon"></i></button>
+							</div>
+						</div>`);
+					});
+					
+				}
 			});
+		}
+
+		function toggleSideMenu(){
+			//$("#sideMenu").toggle();
+			ts('.right.sidebar').sidebar('toggle');
+		}
+
+		function executeEndpoint(object, targetValue=""){
+			var deviceID = $(object).attr("devid");
+			var epd = JSON.parse(decodeURIComponent($(object).attr("epd")));
+			if (epd.Type == "integer" || epd.Type == "float"){
+				//Check if it is in range
+				if (epd.Max && targetValue > epd.Max ){
+					//Snap to max value if over max
+					targetValue = epd.Max;
+				}
+
+				if (epd.Min && targetValue < epd.Min){
+					//Snap to min value if under min
+					targetValue = epd.Min;
+				}
+			}else if (epd.Type == "string"){
+				//Check if regex
+				if (epd.Regex && targetValue.match(stringToRegex(epd.Regex)) == null){
+					//Invalid string input. Reject operation
+					alert("Input string does not match request regex structure: " + epd.Regex);
+					return;
+				}
+			}
+
+			console.log(deviceID, epd, targetValue);
+		}
+
+		const stringToRegex = str => {
+			// Main regex
+			const main = str.match(/\/(.+)\/.*/)[1]
+			
+			// Regex options
+			const options = str.match(/\/.+\/(.*)/)[1]
 			
-		 }
-	});
-}
+			// Compiled regex
+			return new RegExp(main, options)
+		}
+
+		function action(object){
+			//Clear the action form
+			$("#actionForm").html("");
+
+			//Generate the list of endpoints from the device data
+			var device = $(object).parent().parent();
+			var deviceData = device.attr("devicedata");
+			deviceData = JSON.parse(decodeURIComponent(deviceData));
+
+			var epts = deviceData.ControlEndpoints;
+			if (epts.length == 0){
+				//This device has no control endpoints
+
+			}else{
+				epts.forEach(ept => {
+					//Check which type of ept is this. Accept {string, integer, float, bool, none}
+					var encodedEptData = encodeURIComponent(JSON.stringify(ept));
+					var deviceID = deviceData.DeviceUUID;
+					var name = ept.Name;
+					if (ept.Type == "string"){
+						$("#actionForm").append(`<div devid="${deviceID}" epd="${encodedEptData}" class="field">
+							<label>${ept.Desc}</label>
+							<div class="ts action input">
+								<input type="text" placeholder="${name}">
+								<button class="ts primary icon button" title="Send" onclick="executeEndpoint(this.parentNode.parentNode, this.parentNode.parentNode.getElementsByTagName('input')[0].value)"><i class="send icon"></i></button>
+							</div>
+						</div>`);
+					}else if (ept.Type == "integer"){
+						var min = "";
+						var max = "";
+						if (ept.Min != undefined){
+							min = ept.Min;
+						}
+
+						if (ept.Max != undefined){
+							max = ept.Max;
+						}
+
+						$("#actionForm").append(`<div devid="${deviceID}" epd="${encodedEptData}" class="field">
+							<label>${ept.Desc}</label>
+							<div class="ts action input">
+								<input type="number" min="${min}" max="${max}" placeholder="${name}">
+								<button class="ts primary icon button" title="Send" onclick="executeEndpoint(this.parentNode.parentNode, this.parentNode.parentNode.getElementsByTagName('input')[0].value)"><i class="send icon"></i></button>
+							</div>
+						</div>`);
+					}else if (ept.Type == "float"){
+						var min = "";
+						var max = "";
+						var step = "0.1";
+						if (ept.Min != undefined){
+							min = ept.Min;
+						}
+
+						if (ept.Max != undefined){
+							max = ept.Max;
+						}
+
+						if (ept.Steps != undefined){
+							step = ept.Steps;
+						}
+
+						$("#actionForm").append(`<div name="${ept.Name}" devid="${deviceID}" epd="${encodedEptData}" class="field">
+							<label>${ept.Desc}</label>
+							<div class="ts action input">
+								<input type="number" min="${min}" max="${max}" step="${step}" placeholder="${name}">
+								<button class="ts primary icon button" onclick="executeEndpoint(this.parentNode.parentNode, this.parentNode.parentNode.getElementsByTagName('input')[0].value)" title="Send"><i class="send icon"></i></button>
+							</div>
+						</div>`);
+					}else if (ept.Type == "bool"){
+						$("#actionForm").append(`<div name="${ept.Name}" devid="${deviceID}" epd="${encodedEptData}" class="field">
+							<div class="ts toggle checkbox">
+								<input type="checkbox" id="${encodeURIComponent(ept.Name)}" onchange="executeEndpoint(this.parentNode.parentNode, this.value);">
+								<label for="${encodeURIComponent(ept.Name)}">${ept.Name}</label>
+						</div></div>`);
+						
+					}else if (ept.Type == "none"){
+						//No action. (aka just a GET request endpoint)
+						$("#actionForm").append(`<div devid="${deviceID}" epd="${encodedEptData}" class="field">
+							<button class="ts info fluid button" title="${ept.Desc}" onclick="executeEndpoint(this.parentNode);">${name}</button>
+						</div>`);
+					}
+				});
+			}
+			console.log(deviceData);
 
-function toggleSideMenu(){
-	//$("#sideMenu").toggle();
-	ts('.right.sidebar').sidebar('toggle');
-}
+			$("#actioninterface").fadeIn('fast');
+		}
 
-function updateIframeSize(){
-	$("#controlUI").attr("width",$("#actionMainUI").width());
-	$("#controlUI").attr("height",$("#actionMainUI").height());
-	$("#controlUI").css("width",$("#actionMainUI").width());
-	$("#controlUI").css("height",$("#actionMainUI").height());
-}
+		function updateIframeSize(){
+			$("#controlUI").attr("width",$("#actionMainUI").width());
+			$("#controlUI").attr("height",$("#actionMainUI").height());
+			$("#controlUI").css("width",$("#actionMainUI").width());
+			$("#controlUI").css("height",$("#actionMainUI").height());
+		}
 
-$(window).on("resize",function(){
-	updateIframeSize();
-});
-</script>
+		$(window).on("resize",function(){
+			updateIframeSize();
+		});
+	</script>
 </body>
 </html>