index.html 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. <html>
  2. <head>
  3. <meta name="apple-mobile-web-app-capable" content="yes" />
  4. <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
  5. <meta charset="UTF-8">
  6. <title>ArozOS IoT Hub</title>
  7. <link rel="stylesheet" href="../../../script/tocas/tocas.css">
  8. <link rel="manifest" href="manifest.json">
  9. <script src="../../../script/tocas/tocas.js"></script>
  10. <script src="../../../script/jquery.min.js"></script>
  11. <script src="../../../script/ao_module.js"></script>
  12. <style>
  13. .ultrasmall.image{
  14. height:35px;
  15. margin:0px !important;
  16. margin-right:10px !important;
  17. }
  18. .selectable{
  19. cursor:pointer;
  20. }
  21. .selectable:hover{
  22. background-color:#f0f0f0;
  23. }
  24. .noborder{
  25. border: 1px solid transparent !important;
  26. }
  27. .controlBtn{
  28. position:absolute;
  29. right:8px;
  30. bottom:8px;
  31. }
  32. .devIcon{
  33. border-radius: 10px;
  34. }
  35. .primary.button{
  36. background-color: #4aa9eb !important;
  37. }
  38. .bottom.item{
  39. position:absolute;
  40. bottom: 0px;
  41. left:0px;
  42. width:100%;
  43. font-size:80%;
  44. }
  45. #sideMenu{
  46. height: calc(100% - 85px);
  47. }
  48. body{
  49. height:100%;
  50. background:rgba(247,247,247,0.95);
  51. }
  52. </style>
  53. </head>
  54. <body>
  55. <div class="ts right sidebar overlapped vertical menu">
  56. <div class="item">
  57. <div class="ts header">
  58. ArozOS IoT Hub
  59. <div class="sub header">Universal IoT Controller</div>
  60. </div>
  61. </div>
  62. <a class="selectable item" onClick="loadDevList();hideSideMenu();">
  63. <i class="refresh icon"></i> Refresh List
  64. </a>
  65. <a class="selectable item" onClick="scanDevices();hideSideMenu();">
  66. <i class="search icon"></i> Scan Devices
  67. </a>
  68. <a class="selectable item" onClick="manualDriverConfig();">
  69. <i class="edit icon"></i> Manual Device Config
  70. </a>
  71. <a class="selectable item">
  72. <i class="object group icon"></i> Create Action Group
  73. </a>
  74. <div class="bottom item">
  75. CopyRight ArOZ Online Project 2021
  76. </div>
  77. </div>
  78. <div class="pusher">
  79. <div class="ts menu">
  80. <a class="item noborder" href="index.html"><img class="ts ultrasmall circular image" src="img/main_icon.png"> IoT Hub</a>
  81. <a class="right item" onClick="toggleSideMenu();"><i class="content icon"></i></a>
  82. </div>
  83. <div id="devList" class="ts container">
  84. </div>
  85. <br><br><br>
  86. </div>
  87. <div id="moreInfoInterface" class="ts active dimmer" style="display:none;">
  88. <div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#moreInfoInterface").fadeOut("fast");'>
  89. </div>
  90. <div id="informationItnerface" class="ts segment mainUI" style="height:80%;width:95%;overflow-y:auto;">
  91. <div class="ts header">
  92. Device Properties
  93. </div><br>
  94. <div class="ts horizontal form">
  95. <div class="field">
  96. <label>Device UUID</label>
  97. <input id="duid" type="text" readonly="true">
  98. </div>
  99. <div class="field">
  100. <label>Last Seen IP Address</label>
  101. <input id="lastseen" type="text" readonly="true">
  102. </div>
  103. <div class="field">
  104. <label>Device Driver Identifier</label>
  105. <input id="ddi" type="text" readonly="true">
  106. </div>
  107. <div class="field">
  108. <label>Device Information</label>
  109. <input id="dinfo" type="text" readonly="true">
  110. </div>
  111. <div class="field">
  112. <label>Driver Found</label>
  113. <input id="driverfound" type="text" readonly="true">
  114. </div>
  115. </div>
  116. <br>
  117. <button class="ts primary button" onClick='$("#moreInfoInterface").fadeOut("fast");'>Close</button>
  118. <br><br>
  119. </div>
  120. </div>
  121. <div id="actionInterface" class="ts active dimmer" style="display:none;">
  122. <div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#actionInterface").fadeOut("fast");'>
  123. </div>
  124. <div id="actionMainUI" class="ts segment mainUI" style="height:80%;width:95%;">
  125. <iframe id="controlUI" src="" width="500px" height="800px"> </iframe>
  126. </div>
  127. </div>
  128. <div id="nickNameSelector" class="ts active dimmer" style="display:none;">
  129. <div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#nickNameSelector").fadeOut("fast");'>
  130. </div>
  131. <div id="nicknameSelectorUI" class="ts segment mainUI" style="height:80%;width:95%;overflow-y:auto;">
  132. <div class="ts header">
  133. <div class="content">
  134. Nickname Settings
  135. <div class="sub header">Please select an UUID from below for changing its nickname.</div>
  136. </div>
  137. </div>
  138. <div class="ts container">
  139. <div id="nicknameChangeList" class="ts list">
  140. </div>
  141. </div>
  142. </div>
  143. </div>
  144. <div id="manualDevConfig" class="ts active dimmer" style="display:none;">
  145. <div style="position:absolute;width:100%;height:100%;left:0px;top:0px;" onClick='$("#manualDevConfig").fadeOut("fast");'>
  146. </div>
  147. <div id="manualDevConfigUI" class="ts segment mainUI" style="height:80%;width:95%;">
  148. <div class="ts header">
  149. <div class="content">
  150. Manual Device Configuration
  151. <div class="sub header">Add devices that runs other protocol to the system</div>
  152. </div>
  153. </div>
  154. <div class="ts container">
  155. <button class="ts primary tiny button" onClick="addDevViaIP();"><i class="add icon"></i>Add device via IP</button>
  156. <button class="ts tiny button" onClick="openFolderForDev();"><i class="folder icon"></i>Open device folder</button>
  157. <p>Current list of Non-HDS Devices</p>
  158. <div id="customDevList" class="ts ordered list">
  159. <div class="item">Loading</div>
  160. </div>
  161. </div>
  162. </div>
  163. </div>
  164. <div id="loadingMask" class="ts active dimmer" style="display:none;">
  165. <div class="ts text loader">Loading</div>
  166. </div>
  167. <script>
  168. var currentlyViewingDevices = "";
  169. var uselocal = false; //Use Local as command sender or use Host as command sender
  170. var username = $("#data_session_username").text().trim();
  171. //ao_module Float Window functions
  172. ao_module_setWindowIcon("home");
  173. ao_module_setWindowTitle("Home Dynamic Panel");
  174. ao_module_setGlassEffectMode();
  175. ao_module_setWindowSize(465,730,true);
  176. if (!ao_module_virtualDesktop){
  177. $("body").css("background-color","white");
  178. }
  179. var localSetting = ao_module_getStorage("hds","local");
  180. if ( localSetting !== undefined && localSetting !== null && localSetting != ""){
  181. uselocal = (localSetting == "true");
  182. }
  183. if (uselocal){
  184. $('#outdoor').prop('checked', false);
  185. }else{
  186. $('#outdoor').prop('checked', true);
  187. }
  188. //Initiate the page content
  189. loadDevList();
  190. $('#outdoor').change(function() {
  191. if(this.checked){
  192. ao_module_saveStorage("hds","local","false");
  193. uselocal = false;
  194. }else{
  195. ao_module_saveStorage("hds","local","true");
  196. uselocal = true;
  197. }
  198. });
  199. function inputbox(message, placeholder = ""){
  200. var input = prompt(message, placeholder);
  201. if (input != null) {
  202. return input;
  203. }else{
  204. return false;
  205. }
  206. }
  207. function addDevViaIP(){
  208. var ipaddr = inputbox("Please enter the IP Address of your device.");
  209. if (ipaddr != false){
  210. var classType = inputbox("Select the custom driver for this device. Leave empty for default.");
  211. if (classType == false){
  212. alert("Driver Type cannot be empty!");
  213. return;
  214. }
  215. $.get("manualDriverConfig.php?ipaddr=" + ipaddr + "&classType=" + classType,function(data){
  216. //Finished the adding process. Realod the list of custom devices.
  217. loadCustomDeviceList();
  218. });
  219. }else{
  220. //User cancelled the opr
  221. }
  222. }
  223. function openFolderForDev(){
  224. if (ao_module_virtualDesktop){
  225. ao_module_openPath("SystemAOB/system/iotpipe/devices/fixed");
  226. }else{
  227. window.open('../SystemAOB/functions/file_system/index.php?controlLv=2&subdir=SystemAOB/system/iotpipe/devices/fixed');
  228. }
  229. }
  230. function loadCustomDeviceList(){
  231. $("#customDevList").html("");
  232. $.ajax("manualDriverConfig.php").done(function(data){
  233. if (data.length == 0){
  234. $("#customDevList").append('<div class="item">N/A</div>');
  235. }else{
  236. for (var i =0; i < data.length; i++){
  237. $("#customDevList").append('<div class="item">' + data[i][1] + " <br>( Config UID: " + data[i][0] + " / Driver Loader: " + data[i][2] + ")" + '</div>');
  238. }
  239. }
  240. });
  241. }
  242. function manualDriverConfig(){
  243. //Open manual driver configuration interface
  244. loadCustomDeviceList();
  245. $("#manualDevConfig").show();
  246. hideSideMenu();
  247. }
  248. function scanDevices(){
  249. $("#loadingMask").show();
  250. $.ajax("../SystemAOB/system/iotpipe/scandev.php").done(function(data){
  251. if (data.includes("ERROR")){
  252. alert("Scan Error! See console.log for more information.");
  253. console.log(data);
  254. }else{
  255. loadDevList();
  256. }
  257. $("#loadingMask").hide();
  258. });
  259. }
  260. function setSelectNickname(object){
  261. currentlyViewingDevices = $(object).attr("uuid");
  262. setNickname();
  263. }
  264. function hideSideMenu(){
  265. ts('.right.sidebar').sidebar('hide');
  266. }
  267. function showMore(object){
  268. var device = $(object).parent().parent();
  269. var duid = device.attr("uuid");
  270. var lastseen = device.attr("devip");
  271. var ddi = device.attr("classtype");
  272. var dinfo = device.attr("classname");
  273. var dfound = device.attr("driverfound");
  274. if (duid === undefined){
  275. duid = "Unknown";
  276. $("#setNicknameButton").hide();
  277. }else{
  278. $("#setNicknameButton").show();
  279. }
  280. if (dfound === undefined){
  281. dfound = "Offline";
  282. }
  283. $("#duid").val(duid);
  284. $("#lastseen").val(lastseen);
  285. $("#ddi").val(ddi);
  286. $("#dinfo").val(dinfo);
  287. $("#driverfound").val(dfound);
  288. currentlyViewingDevices = duid;
  289. $("#moreInfoInterface").fadeIn('fast');
  290. }
  291. function action(object){
  292. var classType = $(object).parent().parent().attr("classtype");
  293. var driverFound = ($(object).parent().parent().attr("driverfound") == "true");
  294. var ip = $(object).parent().parent().attr("devip");
  295. if (driverFound){
  296. $("#actionInterface").fadeIn('fast');
  297. updateIframeSize();
  298. if ($(object).parent().parent().attr("location") == "remote"){
  299. $("#controlUI").attr("src","../SystemAOB/system/iotpipe/drivers/" + classType + "/" + classType + ".php?ip=" + ip + "&location=remote");
  300. }else{
  301. $("#controlUI").attr("src","../SystemAOB/system/iotpipe/drivers/" + classType + "/" + classType + ".php?ip=" + ip);
  302. }
  303. }else{
  304. alert("Driver not found!");
  305. }
  306. }
  307. function loadDevList(){
  308. $("#devList").html("");
  309. var template = '<div class="ts segment HDSDev" devIp="{deviceIP}" location="local">\
  310. <div class="ts grid">\
  311. <div class="four wide column"><img class="ts tiny devIcon image" src="img/system/loading.gif"></div>\
  312. <div class="twelve wide column">\
  313. <div class="ts container">\
  314. <div class="ts header">\
  315. <span class="devHeader">{deviceIP}</span>\
  316. <div class="sub devProperty header"><i class="spinner loading icon"></i> Loading</div>\
  317. </div>\
  318. </div>\
  319. </div>\
  320. </div>\
  321. <div class="controlBtn infoMount">\
  322. <button class="ts icon button" onClick="showMore(this);"><i class="notice icon"></i></button>\
  323. <button class="ts primary icon button" onClick="action(this);"><i class="external icon"></i></button>\
  324. </div>\
  325. </div>';
  326. $.ajax("loadDevList.php").done(function(data){
  327. if (data.length == 0){
  328. var nodevFound = '<div class="ts segment">\
  329. <h5 class="ts center aligned icon header">\
  330. <i class="remove icon"></i>No Device Found\
  331. <div class="sub header">No HDS based device is found in your network.<br>\
  332. Click <a href="readmore.html">here</a> to know more on how to build one yourself.</div>\
  333. </h5>\
  334. </div>';
  335. $("#devList").append(nodevFound);
  336. }else{
  337. for (var i =0; i < data.length; i++){
  338. var ip = data[i];
  339. var box = template;
  340. box = box.split("{deviceIP}").join(ip);
  341. $("#devList").append(box);
  342. }
  343. }
  344. //All devices loaded. Get information about the devices.
  345. $(".HDSDev").each(function(){
  346. let ip = $(this).attr("devIp");
  347. requestInfo(ip,"info",this,uselocal);
  348. requestUUID(ip,"uuid",this,uselocal);
  349. });
  350. });
  351. $.ajax("manualDriverConfig.php").done(function(data){
  352. for(var i =0; i < data.length; i++){
  353. var uuid = data[i][0];
  354. var ipaddr = data[i][1];
  355. var classType = data[i][2];
  356. var box = template;
  357. box = box.split("{deviceIP}").join(ipaddr);
  358. box = $(box).attr("uuid",uuid);
  359. box = $(box).attr("classtype",classType);
  360. box = $(box).attr("classname",classType.split(".").join(" "));
  361. box = $(box).removeClass("HDSDev").addClass("CustomDev");
  362. box = $(box).attr("location","fixed");
  363. $("#devList").append(box);
  364. }
  365. initCustomDevUI();
  366. });
  367. }
  368. function initCustomDevUI(){
  369. $(".CustomDev").each(function(){
  370. var classType = $(this).attr("classtype");
  371. loadDevImage(classType,this);
  372. loadDevDefaultDescription(this);
  373. getNickName(this);
  374. });
  375. }
  376. function loadDevDefaultDescription(object){
  377. var classType = $(object).attr("classtype");
  378. $.ajax("loadDriverProperties.php?classType=" + classType).done(function(data){
  379. $(object).find(".devProperty").text(data);
  380. });
  381. }
  382. function requestUUID(ip,subpath, object,local){
  383. if (local){
  384. //use local device as controller
  385. $.ajax({
  386. url: "http://" + ip + "/" + subpath,
  387. error: function(){
  388. //Declare offline
  389. },
  390. success: function(data){
  391. //UUID found.
  392. var uuid = data;
  393. $(object).attr("uuid",uuid);
  394. $(object).find(".devHeader").text(uuid);
  395. $(object).attr('location',"local");
  396. getNickName(object);
  397. },
  398. timeout: 5000 // sets timeout to 3 seconds
  399. });
  400. }else{
  401. //use host server as controller
  402. $.ajax({
  403. url:"../SystemAOB/system/iotpipe/extreq.php?ipa=" + ip + "&subpath=" + subpath,
  404. error: function(){
  405. //This devices might be not on the server side. Try again with client side request
  406. requestUUID(ip,subpath, object,false);
  407. },
  408. success: function(data){
  409. let filename = data;
  410. let thisObject = object;
  411. setTimeout(function(){
  412. tryGetUUID(filename,ip,subpath,thisObject,local);
  413. },1300);
  414. }
  415. }
  416. );
  417. }
  418. }
  419. function tryGetUUID(filename,ip,subpath, object,local,retryCount=0){
  420. if (retryCount > 10){
  421. //Assume offline
  422. return;
  423. }
  424. $.get("../SystemAOB/system/iotpipe/extreq.php?getreq=" + filename,function(data){
  425. if (data.includes("ERROR")){
  426. retryCount++;
  427. setTimeout(function(){
  428. tryGetUUID(filename,ip,subpath, object,local,retryCount);
  429. },1300);
  430. }else{
  431. var uuid = data;
  432. $(object).attr("uuid",uuid);
  433. $(object).attr('location',"remote");
  434. $(object).find(".devHeader").text(uuid);
  435. getNickName(object);
  436. }
  437. });
  438. }
  439. function getNickName(object){
  440. $.ajax({
  441. url: "nicknameman.php?uuid=" + $(object).attr("uuid"),
  442. success: function(data){
  443. //UUID found.
  444. if (data != false){
  445. //Replace the uuid with nickname
  446. $(object).find(".devHeader").text(data);
  447. $(object).attr("nickname",data);
  448. }
  449. },
  450. timeout: 5000 // sets timeout to 3 seconds
  451. });
  452. }
  453. function requestInfo(ip,subpath,object,local=true){
  454. //This function should work if both devices are in the same subnet. If not, something else will be done.
  455. if (local){
  456. $.ajax({
  457. url: "http://" + ip + "/" + subpath,
  458. error: function(){
  459. //Declare offline
  460. $(object).attr("className","offline");
  461. $(object).attr("classType","offline");
  462. $(object).find(".devHeader").html("<i class='remove icon'></i> Unable to Connect");
  463. $(object).find(".devProperty").html("This device is offline or its address has been changed.");
  464. $(object).find(".devIcon").attr('src',"img/system/unable2connect.png");
  465. },
  466. success: function(data){
  467. //Device in the same subnet. Try to load driver.
  468. if (data.includes("_")){
  469. var className = data.split("_")[0];
  470. var classType = data.split("_")[1];
  471. $(object).attr("className",className);
  472. $(object).attr("classType",classType);
  473. $(object).find(".devProperty").html(className);
  474. loadDevImage(classType,object);
  475. }else{
  476. console.log("[Homdynm] Error. Unknown devices class for ip address: " + $(object).attr("devIP"));
  477. }
  478. },
  479. timeout: 5000 // sets timeout to 3 seconds
  480. });
  481. }else{
  482. //use server to get the required information.
  483. $.ajax({
  484. url:"../SystemAOB/system/iotpipe/extreq.php?ipa=" + ip + "&subpath=" + subpath,
  485. error: function(){
  486. //This devices might be not on the server side. Try again with client side request
  487. requestInfo(ip,subpath,object,true);
  488. },
  489. success: function(data){
  490. //The data should be a filename for getreq. Get it after 2 sec
  491. let filename = data;
  492. let thisip = ip;
  493. let thisObject = object;
  494. setTimeout(function(){
  495. tryGetReqInfo(filename,ip,subpath,thisObject,false);
  496. },1000);
  497. },
  498. timeout: 5000 // sets timeout to 3 seconds
  499. })
  500. }
  501. }
  502. function tryGetReqInfo(filename,ip,subpath,thisObject,local,retryCount = 0){
  503. if (retryCount > 10){
  504. //Assume offline
  505. $(thisObject).attr("className","offline");
  506. $(thisObject).attr("classType","offline");
  507. $(thisObject).find(".devHeader").html("<i class='remove icon'></i> Unable to Connect");
  508. $(thisObject).find(".devProperty").html("This device is offline or its address has been changed.");
  509. $(thisObject).find(".devIcon").attr('src',"img/system/unable2connect.png");
  510. return;
  511. }
  512. $.get("../SystemAOB/system/iotpipe/extreq.php?getreq=" + filename,function(data){
  513. console.log("[Home Dynamic] Remote returned value: " + ip + " " + data);
  514. if (data.includes("ERROR")){
  515. retryCount++;
  516. setTimeout(function(){
  517. tryGetReqInfo(filename,ip,subpath,thisObject,local,retryCount)
  518. },1000);
  519. }else{
  520. //Device found on server side. Get the information.
  521. if (data.includes(".") && data.includes("_")){
  522. //Found! Do something here
  523. var className = data.split("_")[0];
  524. var classType = data.split("_")[1];
  525. $(thisObject).attr("className",className);
  526. $(thisObject).attr("classType",classType);
  527. $(thisObject).find(".devProperty").html(className);
  528. loadDevImage(classType,thisObject);
  529. }else{
  530. //Might be trash from a random web server. Just ignore them.
  531. }
  532. }
  533. });
  534. }
  535. function loadDevImage(classType,object){
  536. $.ajax("loadDevImage.php?driverClass=" + classType).done(function(data){
  537. $(object).find(".devIcon").attr('src',data[0]);
  538. $(object).attr("driverFound",data[1]);
  539. if (data[1] == false){
  540. //Driver not found. Update the icon
  541. $(object).find(".devIcon").attr('src',"img/system/driverNotFound.png");
  542. }
  543. });
  544. }
  545. function toggleSideMenu(){
  546. //$("#sideMenu").toggle();
  547. ts('.right.sidebar').sidebar('toggle');
  548. }
  549. function updateIframeSize(){
  550. $("#controlUI").attr("width",$("#actionMainUI").width());
  551. $("#controlUI").attr("height",$("#actionMainUI").height());
  552. $("#controlUI").css("width",$("#actionMainUI").width());
  553. $("#controlUI").css("height",$("#actionMainUI").height());
  554. }
  555. $(window).on("resize",function(){
  556. updateIframeSize();
  557. });
  558. </script>
  559. </body>
  560. </html>