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.

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