Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

366 lines
9.7 KiB

8 years ago
7 years ago
7 years ago
8 years ago
8 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
8 years ago
  1. var websock;
  2. var password = false;
  3. var maxNetworks;
  4. // http://www.the-art-of-web.com/javascript/validate-password/
  5. function checkPassword(str) {
  6. // at least one number, one lowercase and one uppercase letter
  7. // at least eight characters that are letters, numbers or the underscore
  8. var re = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])\w{8,}$/;
  9. return re.test(str);
  10. }
  11. function validateForm() {
  12. var form = $("#formSave");
  13. // password
  14. var adminPass1 = $("input[name='adminPass1']", form).val();
  15. if (adminPass1.length > 0 && !checkPassword(adminPass1)) {
  16. alert("The password you have entered is not valid, it must have at least 8 characters, 1 lower and 1 uppercase and 1 number!");
  17. return false;
  18. }
  19. var adminPass2 = $("input[name='adminPass2']", form).val();
  20. if (adminPass1 != adminPass2) {
  21. alert("Passwords are different!");
  22. return false;
  23. }
  24. return true;
  25. }
  26. function doUpdate() {
  27. if (validateForm()) {
  28. var data = $("#formSave").serializeArray();
  29. websock.send(JSON.stringify({'config': data}));
  30. $(".powExpected").val(0);
  31. }
  32. return false;
  33. }
  34. function doReset() {
  35. var response = window.confirm("Are you sure you want to reset the device?");
  36. if (response == false) return false;
  37. websock.send(JSON.stringify({'action': 'reset'}));
  38. return false;
  39. }
  40. function doReconnect() {
  41. var response = window.confirm("Are you sure you want to disconnect from the current WIFI network?");
  42. if (response == false) return false;
  43. websock.send(JSON.stringify({'action': 'reconnect'}));
  44. return false;
  45. }
  46. function doToggle(element, value) {
  47. var relayID = parseInt(element.attr("data"));
  48. websock.send(JSON.stringify({'action': value ? 'on' : 'off', 'relayID': relayID}));
  49. return false;
  50. }
  51. function randomString(length, chars) {
  52. var mask = '';
  53. if (chars.indexOf('a') > -1) mask += 'abcdefghijklmnopqrstuvwxyz';
  54. if (chars.indexOf('A') > -1) mask += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  55. if (chars.indexOf('#') > -1) mask += '0123456789';
  56. if (chars.indexOf('@') > -1) mask += 'ABCDEF';
  57. if (chars.indexOf('!') > -1) mask += '~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\';
  58. var result = '';
  59. for (var i = length; i > 0; --i) result += mask[Math.round(Math.random() * (mask.length - 1))];
  60. return result;
  61. }
  62. function doGenerateAPIKey() {
  63. var apikey = randomString(16, '@#');
  64. $("input[name=\"apiKey\"]").val(apikey);
  65. return false;
  66. }
  67. function showPanel() {
  68. $(".panel").hide();
  69. $("#" + $(this).attr("data")).show();
  70. if ($("#layout").hasClass('active')) toggleMenu();
  71. $("input[type='checkbox']").iphoneStyle("calculateDimensions").iphoneStyle("refresh");
  72. };
  73. function toggleMenu() {
  74. $("#layout").toggleClass('active');
  75. $("#menu").toggleClass('active');
  76. $("#menuLink").toggleClass('active');
  77. }
  78. function createRelays(count) {
  79. var current = $("#relays > div").length;
  80. if (current > 0) return;
  81. var template = $("#relayTemplate .pure-g")[0];
  82. for (var relayID=0; relayID<count; relayID++) {
  83. var line = $(template).clone();
  84. $(line).find("input").each(function() {
  85. $(this).attr("data", relayID);
  86. });
  87. if (count > 1) $(".relay_id", line).html(" " + (relayID+1));
  88. line.appendTo("#relays");
  89. $(":checkbox", line).iphoneStyle({
  90. onChange: doToggle,
  91. resizeContainer: true,
  92. resizeHandle: true,
  93. checkedLabel: 'ON',
  94. uncheckedLabel: 'OFF'
  95. });
  96. }
  97. }
  98. function createIdxs(count) {
  99. var current = $("#idxs > div").length;
  100. if (current > 0) return;
  101. var template = $("#idxTemplate .pure-g")[0];
  102. for (var id=0; id<count; id++) {
  103. var line = $(template).clone();
  104. $(line).find("input").each(function() {
  105. $(this).attr("data", id).attr("tabindex", 43+id);
  106. });
  107. if (count > 1) $(".id", line).html(" " + id);
  108. line.appendTo("#idxs");
  109. }
  110. }
  111. function delNetwork() {
  112. var parent = $(this).parents(".pure-g");
  113. $(parent).remove();
  114. }
  115. function moreNetwork() {
  116. var parent = $(this).parents(".pure-g");
  117. $("div.more", parent).toggle();
  118. }
  119. function addNetwork() {
  120. var numNetworks = $("#networks > div").length;
  121. if (numNetworks >= maxNetworks) {
  122. alert("Max number of networks reached");
  123. return;
  124. }
  125. var tabindex = 200 + numNetworks * 10;
  126. var template = $("#networkTemplate").children();
  127. var line = $(template).clone();
  128. $(line).find("input").each(function() {
  129. $(this).attr("tabindex", tabindex++);
  130. });
  131. $(line).find(".button-del-network").on('click', delNetwork);
  132. $(line).find(".button-more-network").on('click', moreNetwork);
  133. line.appendTo("#networks");
  134. return line;
  135. }
  136. function processData(data) {
  137. // title
  138. if ("app" in data) {
  139. var title = data.app;
  140. if ("version" in data) {
  141. title = title + " " + data.version;
  142. }
  143. $(".pure-menu-heading").html(title);
  144. if ("hostname" in data) {
  145. title = data.hostname + " - " + title;
  146. }
  147. document.title = title;
  148. }
  149. Object.keys(data).forEach(function(key) {
  150. // Actions
  151. if (key == "action") {
  152. if (data.action == "reload") {
  153. if (password) {
  154. // Forget current authentication
  155. $.ajax({
  156. 'method': 'GET',
  157. 'url': '/',
  158. 'async': false,
  159. 'username': "logmeout",
  160. 'password': "123456",
  161. 'headers': { "Authorization": "Basic xxx" }
  162. }).done(function(data) {
  163. // If we don't get an error, we actually got an error as we expect an 401!
  164. }).fail(function(){
  165. // We expect to get an 401 Unauthorized error! In this case we are successfully
  166. // logged out and we redirect the user.
  167. window.location = "/";
  168. });
  169. }
  170. }
  171. return;
  172. }
  173. if (key == "maxNetworks") {
  174. maxNetworks = parseInt(data.maxNetworks);
  175. return;
  176. }
  177. // Wifi
  178. if (key == "wifi") {
  179. var networks = data.wifi;
  180. for (var i in networks) {
  181. // add a new row
  182. var line = addNetwork();
  183. // fill in the blanks
  184. var wifi = data.wifi[i];
  185. Object.keys(wifi).forEach(function(key) {
  186. var element = $("input[name=" + key + "]", line);
  187. if (element.length) element.val(wifi[key]);
  188. });
  189. }
  190. return;
  191. }
  192. // Relay status
  193. if (key == "relayStatus") {
  194. var relays = data.relayStatus;
  195. createRelays(relays.length);
  196. for (var relayID in relays) {
  197. var element = $(".relayStatus[data=" + relayID + "]");
  198. if (element.length > 0) {
  199. element
  200. .prop("checked", relays[relayID])
  201. .iphoneStyle("refresh");
  202. }
  203. }
  204. return;
  205. }
  206. // Domoticz
  207. if (key == "dczIdx") {
  208. var idxs = data.dczIdx;
  209. createIdxs(idxs.length);
  210. for (var i in idxs) {
  211. var element = $(".dczIdx[data=" + i + "]");
  212. if (element.length > 0) element.val(idxs[i]);
  213. }
  214. return;
  215. }
  216. // Messages
  217. if (key == "message") {
  218. window.alert(data.message);
  219. return;
  220. }
  221. // Enable options
  222. if (key.endsWith("Visible")) {
  223. var module = key.slice(0,-7);
  224. $(".module-" + module).show();
  225. return;
  226. }
  227. // Pre-process
  228. if (key == "network") {
  229. data.network = data.network.toUpperCase();
  230. }
  231. if (key == "mqttStatus") {
  232. data.mqttStatus = data.mqttStatus ? "CONNECTED" : "NOT CONNECTED";
  233. }
  234. // Look for INPUTs
  235. var element = $("input[name=" + key + "]");
  236. if (element.length > 0) {
  237. if (element.attr('type') == 'checkbox') {
  238. element
  239. .prop("checked", data[key])
  240. .iphoneStyle("refresh");
  241. } else {
  242. element.val(data[key]);
  243. }
  244. return;
  245. }
  246. // Look for SELECTs
  247. var element = $("select[name=" + key + "]");
  248. if (element.length > 0) {
  249. element.val(data[key]);
  250. return;
  251. }
  252. });
  253. // Auto generate an APIKey if none defined yet
  254. if ($("input[name='apiKey']").val() == "") {
  255. doGenerateAPIKey();
  256. }
  257. }
  258. function getJson(str) {
  259. try {
  260. return JSON.parse(str);
  261. } catch (e) {
  262. return false;
  263. }
  264. }
  265. function initWebSocket(host) {
  266. if (host === undefined) {
  267. host = window.location.hostname;
  268. }
  269. websock = new WebSocket('ws://' + host + '/ws');
  270. websock.onopen = function(evt) {};
  271. websock.onclose = function(evt) {};
  272. websock.onerror = function(evt) {};
  273. websock.onmessage = function(evt) {
  274. var data = getJson(evt.data);
  275. if (data) processData(data);
  276. };
  277. }
  278. function init() {
  279. $("#menuLink").on('click', toggleMenu);
  280. $(".button-update").on('click', doUpdate);
  281. $(".button-reset").on('click', doReset);
  282. $(".button-reconnect").on('click', doReconnect);
  283. $(".button-apikey").on('click', doGenerateAPIKey);
  284. $(".pure-menu-link").on('click', showPanel);
  285. $(".button-add-network").on('click', addNetwork);
  286. $.ajax({
  287. 'method': 'GET',
  288. 'url': '/auth'
  289. }).done(function(data) {
  290. initWebSocket();
  291. });
  292. }
  293. $(init);