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.

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