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.

459 lines
12 KiB

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