code.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303
  1. // First, checks if it isn't implemented yet.
  2. if (!String.prototype.format) {
  3. String.prototype.format = function() {
  4. var args = arguments;
  5. return this.replace(/{(\d+)}/g, function(match, number) {
  6. return typeof args[number] != 'undefined'
  7. ? args[number]
  8. : match
  9. ;
  10. });
  11. };
  12. }
  13. var nvs_type_t = {
  14. NVS_TYPE_U8 : 0x01, /*!< Type uint8_t */
  15. NVS_TYPE_I8 : 0x11, /*!< Type int8_t */
  16. NVS_TYPE_U16 : 0x02, /*!< Type uint16_t */
  17. NVS_TYPE_I16 : 0x12, /*!< Type int16_t */
  18. NVS_TYPE_U32 : 0x04, /*!< Type uint32_t */
  19. NVS_TYPE_I32 : 0x14, /*!< Type int32_t */
  20. NVS_TYPE_U64 : 0x08, /*!< Type uint64_t */
  21. NVS_TYPE_I64 : 0x18, /*!< Type int64_t */
  22. NVS_TYPE_STR : 0x21, /*!< Type string */
  23. NVS_TYPE_BLOB : 0x42, /*!< Type blob */
  24. NVS_TYPE_ANY : 0xff /*!< Must be last */
  25. } ;
  26. var task_state_t = {
  27. 0 : "eRunning", /*!< A task is querying the state of itself, so must be running. */
  28. 1 : "eReady", /*!< The task being queried is in a read or pending ready list. */
  29. 2 : "eBlocked", /*!< The task being queried is in the Blocked state. */
  30. 3 : "eSuspended", /*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
  31. 4 : "eDeleted"
  32. }
  33. var escapeHTML = function(unsafe) {
  34. return unsafe.replace(/[&<"']/g, function(m) {
  35. switch (m) {
  36. case '&':
  37. return '&amp;';
  38. case '<':
  39. return '&lt;';
  40. case '"':
  41. return '&quot;';
  42. default:
  43. return '&#039;';
  44. }
  45. });
  46. };
  47. var releaseURL = 'https://api.github.com/repos/sle118/squeezelite-esp32/releases';
  48. var recovery = false;
  49. var enableAPTimer = true;
  50. var enableStatusTimer = true;
  51. var commandHeader = 'squeezelite -b 500:2000 -d all=info -C 30 -W';
  52. var pname, ver, otapct, otadsc;
  53. var blockAjax = false;
  54. var blockFlashButton = false;
  55. var dblclickCounter=0;
  56. var apList = null;
  57. var selectedSSID = "";
  58. var refreshAPInterval = null;
  59. var checkStatusInterval = null;
  60. var StatusIntervalActive = false;
  61. var RefreshAPIIntervalActive = false;
  62. var LastRecoveryState=null;
  63. var LastCommandsState=null;
  64. var output = '';
  65. function delay_msg(t, v) {
  66. return new Promise(function(resolve) {
  67. setTimeout(resolve.bind(null, v), t)
  68. });
  69. }
  70. Promise.prototype.delay = function(t) {
  71. return this.then(function(v) {
  72. return delay_msg(t, v);
  73. });
  74. }
  75. function stopCheckStatusInterval(){
  76. if(checkStatusInterval != null){
  77. clearTimeout(checkStatusInterval);
  78. checkStatusInterval = null;
  79. }
  80. StatusIntervalActive = false;
  81. }
  82. function stopRefreshAPInterval(){
  83. if(refreshAPInterval != null){
  84. clearTimeout(refreshAPInterval);
  85. refreshAPInterval = null;
  86. }
  87. RefreshAPIIntervalActive = false;
  88. }
  89. function startCheckStatusInterval(){
  90. StatusIntervalActive = true;
  91. checkStatusInterval = setTimeout(checkStatus, 3000);
  92. }
  93. function startRefreshAPInterval(){
  94. RefreshAPIIntervalActive = true;
  95. refreshAPInterval = setTimeout(refreshAP(false), 4500); // leave enough time for the initial scan
  96. }
  97. function RepeatCheckStatusInterval(){
  98. if(StatusIntervalActive)
  99. startCheckStatusInterval();
  100. }
  101. function RepeatRefreshAPInterval(){
  102. if(RefreshAPIIntervalActive)
  103. startRefreshAPInterval();
  104. }
  105. function getConfigJson(slimMode){
  106. var config = {};
  107. $("input.nvs").each(function() {
  108. var key = $(this)[0].id;
  109. var val = $(this).val();
  110. if(!slimMode){
  111. var nvs_type = parseInt($(this)[0].attributes.nvs_type.nodeValue,10);
  112. if (key != '') {
  113. config[key] = {};
  114. if(nvs_type == nvs_type_t.NVS_TYPE_U8
  115. || nvs_type == nvs_type_t.NVS_TYPE_I8
  116. || nvs_type == nvs_type_t.NVS_TYPE_U16
  117. || nvs_type == nvs_type_t.NVS_TYPE_I16
  118. || nvs_type == nvs_type_t.NVS_TYPE_U32
  119. || nvs_type == nvs_type_t.NVS_TYPE_I32
  120. || nvs_type == nvs_type_t.NVS_TYPE_U64
  121. || nvs_type == nvs_type_t.NVS_TYPE_I64) {
  122. config[key].value = parseInt(val);
  123. }
  124. else {
  125. config[key].value = val;
  126. }
  127. config[key].type = nvs_type;
  128. }
  129. }
  130. else {
  131. config[key] = val;
  132. }
  133. });
  134. var key = $("#nvs-new-key").val();
  135. var val = $("#nvs-new-value").val();
  136. if (key != '') {
  137. if(!slimMode){
  138. config[key] = {};
  139. config[key].value = val;
  140. config[key].type = 33;
  141. }
  142. else {
  143. config[key] = val;
  144. }
  145. }
  146. return config;
  147. }
  148. function onFileLoad(elementId, event) {
  149. var data={};
  150. try{
  151. data = JSON.parse(elementId.srcElement.result);
  152. }
  153. catch (e){
  154. alert('Parsing failed!\r\n '+ e);
  155. }
  156. $("input.nvs").each(function() {
  157. var key = $(this)[0].id;
  158. var val = $(this).val();
  159. if(data[key]){
  160. if(data[key] != val){
  161. console.log("Changed "& key & " " & val & "==>" & data[key]);
  162. $(this).val(data[key]);
  163. }
  164. }
  165. else {
  166. console.log("Value " & key & " missing from file");
  167. }
  168. });
  169. }
  170. function onChooseFile(event, onLoadFileHandler) {
  171. if (typeof window.FileReader !== 'function')
  172. throw ("The file API isn't supported on this browser.");
  173. let input = event.target;
  174. if (!input)
  175. throw ("The browser does not properly implement the event object");
  176. if (!input.files)
  177. throw ("This browser does not support the `files` property of the file input.");
  178. if (!input.files[0])
  179. return undefined;
  180. let file = input.files[0];
  181. let fr = new FileReader();
  182. fr.onload = onLoadFileHandler;
  183. fr.readAsText(file);
  184. input.value="";
  185. }
  186. $(document).ready(function(){
  187. $("input#show-commands")[0].checked=LastCommandsState==1?true:false;
  188. $('a[href^="#tab-commands"]').hide();
  189. $("#load-nvs").click(function () {
  190. $("#nvsfilename").trigger('click');
  191. });
  192. $("#wifi-status").on("click", ".ape", function() {
  193. $( "#wifi" ).slideUp( "fast", function() {});
  194. $( "#connect-details" ).slideDown( "fast", function() {});
  195. });
  196. $("#manual_add").on("click", ".ape", function() {
  197. selectedSSID = $(this).text();
  198. $( "#ssid-pwd" ).text(selectedSSID);
  199. $( "#wifi" ).slideUp( "fast", function() {});
  200. $( "#connect_manual" ).slideDown( "fast", function() {});
  201. $( "#connect" ).slideUp( "fast", function() {});
  202. //update wait screen
  203. $( "#loading" ).show();
  204. $( "#connect-success" ).hide();
  205. $( "#connect-fail" ).hide();
  206. });
  207. $("#wifi-list").on("click", ".ape", function() {
  208. selectedSSID = $(this).text();
  209. $( "#ssid-pwd" ).text(selectedSSID);
  210. $( "#wifi" ).slideUp( "fast", function() {});
  211. $( "#connect_manual" ).slideUp( "fast", function() {});
  212. $( "#connect" ).slideDown( "fast", function() {});
  213. //update wait screen
  214. $( "#loading" ).show();
  215. $( "#connect-success" ).hide();
  216. $( "#connect-fail" ).hide();
  217. });
  218. $("#cancel").on("click", function() {
  219. selectedSSID = "";
  220. $( "#connect" ).slideUp( "fast", function() {});
  221. $( "#connect_manual" ).slideUp( "fast", function() {});
  222. $( "#wifi" ).slideDown( "fast", function() {});
  223. });
  224. $("#manual_cancel").on("click", function() {
  225. selectedSSID = "";
  226. $( "#connect" ).slideUp( "fast", function() {});
  227. $( "#connect_manual" ).slideUp( "fast", function() {});
  228. $( "#wifi" ).slideDown( "fast", function() {});
  229. });
  230. $("#join").on("click", function() {
  231. performConnect();
  232. });
  233. $("#manual_join").on("click", function() {
  234. performConnect($(this).data('connect'));
  235. });
  236. $("#ok-details").on("click", function() {
  237. $( "#connect-details" ).slideUp( "fast", function() {});
  238. $( "#wifi" ).slideDown( "fast", function() {});
  239. });
  240. $("#ok-credits").on("click", function() {
  241. $( "#credits" ).slideUp( "fast", function() {});
  242. $( "#app" ).slideDown( "fast", function() {});
  243. });
  244. $("#acredits").on("click", function(event) {
  245. event.preventDefault();
  246. $( "#app" ).slideUp( "fast", function() {});
  247. $( "#credits" ).slideDown( "fast", function() {});
  248. });
  249. $("#ok-connect").on("click", function() {
  250. $( "#connect-wait" ).slideUp( "fast", function() {});
  251. $( "#wifi" ).slideDown( "fast", function() {});
  252. });
  253. $("#disconnect").on("click", function() {
  254. $( "#connect-details-wrap" ).addClass('blur');
  255. $( "#diag-disconnect" ).slideDown( "fast", function() {});
  256. });
  257. $("#no-disconnect").on("click", function() {
  258. $( "#diag-disconnect" ).slideUp( "fast", function() {});
  259. $( "#connect-details-wrap" ).removeClass('blur');
  260. });
  261. $("#yes-disconnect").on("click", function() {
  262. stopCheckStatusInterval();
  263. selectedSSID = "";
  264. $( "#diag-disconnect" ).slideUp( "fast", function() {});
  265. $( "#connect-details-wrap" ).removeClass('blur');
  266. $.ajax({
  267. url: '/connect.json',
  268. dataType: 'text',
  269. method: 'DELETE',
  270. cache: false,
  271. contentType: 'application/json; charset=utf-8',
  272. data: JSON.stringify({ 'timestamp': Date.now()})
  273. });
  274. startCheckStatusInterval();
  275. $( "#connect-details" ).slideUp( "fast", function() {});
  276. $( "#wifi" ).slideDown( "fast", function() {})
  277. });
  278. $("input#show-commands").on("click", function() {
  279. this.checked=this.checked?1:0;
  280. if(this.checked){
  281. $('a[href^="#tab-commands"]').show();
  282. LastCommandsState = 1;
  283. } else {
  284. LastCommandsState = 0;
  285. $('a[href^="#tab-commands"]').hide();
  286. }
  287. });
  288. $("input#show-nvs").on("click", function() {
  289. this.checked=this.checked?1:0;
  290. if(this.checked){
  291. $('a[href^="#tab-nvs"]').show();
  292. } else {
  293. $('a[href^="#tab-nvs"]').hide();
  294. }
  295. });
  296. $("input#autoexec-cb").on("click", function() {
  297. var data = { 'timestamp': Date.now() };
  298. autoexec = (this.checked)?"1":"0";
  299. data['config'] = {};
  300. data['config'] = {
  301. autoexec : {
  302. value : autoexec,
  303. type : 33
  304. }
  305. }
  306. showMessage('please wait for the ESP32 to reboot', 'MESSAGING_WARNING');
  307. $.ajax({
  308. url: '/config.json',
  309. dataType: 'text',
  310. method: 'POST',
  311. cache: false,
  312. // headers: { "X-Custom-autoexec": autoexec },
  313. contentType: 'application/json; charset=utf-8',
  314. data: JSON.stringify(data),
  315. error: function (xhr, ajaxOptions, thrownError) {
  316. console.log(xhr.status);
  317. console.log(thrownError);
  318. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  319. },
  320. complete: function(response) {
  321. //var returnedResponse = JSON.parse(response.responseText);
  322. console.log(response.responseText);
  323. console.log('sent config JSON with headers:', autoexec);
  324. console.log('now triggering reboot');
  325. $.ajax({
  326. url: '/reboot_ota.json',
  327. dataType: 'text',
  328. method: 'POST',
  329. cache: false,
  330. contentType: 'application/json; charset=utf-8',
  331. data: JSON.stringify({ 'timestamp': Date.now()}),
  332. error: function (xhr, ajaxOptions, thrownError) {
  333. console.log(xhr.status);
  334. console.log(thrownError);
  335. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  336. },
  337. complete: function(response) {
  338. console.log('reboot call completed');
  339. }
  340. });
  341. }
  342. });
  343. });
  344. $("input#save-autoexec1").on("click", function() {
  345. var data = { 'timestamp': Date.now() };
  346. autoexec1 = $("#autoexec1").val();
  347. data['config'] = {};
  348. data['config'] = {
  349. autoexec1 : {
  350. value : autoexec1,
  351. type : 33
  352. }
  353. }
  354. $.ajax({
  355. url: '/config.json',
  356. dataType: 'text',
  357. method: 'POST',
  358. cache: false,
  359. // headers: { "X-Custom-autoexec1": autoexec1 },
  360. contentType: 'application/json; charset=utf-8',
  361. data: JSON.stringify(data),
  362. error: function (xhr, ajaxOptions, thrownError) {
  363. console.log(xhr.status);
  364. console.log(thrownError);
  365. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  366. }
  367. });
  368. console.log('sent config JSON with headers:', autoexec1);
  369. console.log('sent data:', JSON.stringify(data));
  370. });
  371. $("input#save-gpio").on("click", function() {
  372. var data = { 'timestamp': Date.now() };
  373. var config = {};
  374. var headers = {};
  375. $("input.gpio").each(function() {
  376. var id = $(this)[0].id;
  377. var pin = $(this).val();
  378. if (pin != '') {
  379. config[id] = {};
  380. config[id].value = pin;
  381. config[id].type = nvs_type_t.NVS_TYPE_STR;
  382. }
  383. });
  384. data['config'] = config;
  385. $.ajax({
  386. url: '/config.json',
  387. dataType: 'text',
  388. method: 'POST',
  389. cache: false,
  390. headers: headers,
  391. contentType: 'application/json; charset=utf-8',
  392. data: JSON.stringify(data),
  393. error: function (xhr, ajaxOptions, thrownError) {
  394. console.log(xhr.status);
  395. console.log(thrownError);
  396. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  397. }
  398. });
  399. console.log('sent config JSON with headers:', JSON.stringify(headers));
  400. console.log('sent config JSON with data:', JSON.stringify(data));
  401. });
  402. $("#save-as-nvs").on("click", function() {
  403. var data = { 'timestamp': Date.now() };
  404. var config = getConfigJson(true);
  405. const a = document.createElement("a");
  406. a.href = URL.createObjectURL(
  407. new Blob([JSON.stringify(config, null, 2)], {
  408. type: "text/plain"
  409. }));
  410. a.setAttribute("download", "nvs_config" + Date.now() +"json");
  411. document.body.appendChild(a);
  412. a.click();
  413. document.body.removeChild(a);
  414. console.log('sent config JSON with headers:', JSON.stringify(headers));
  415. console.log('sent config JSON with data:', JSON.stringify(data));
  416. });
  417. $("#save-nvs").on("click", function() {
  418. var headers = {};
  419. var data = { 'timestamp': Date.now() };
  420. var config = getConfigJson(false);
  421. data['config'] = config;
  422. $.ajax({
  423. url: '/config.json',
  424. dataType: 'text',
  425. method: 'POST',
  426. cache: false,
  427. headers: headers,
  428. contentType: 'application/json; charset=utf-8',
  429. data : JSON.stringify(data),
  430. error: function (xhr, ajaxOptions, thrownError) {
  431. console.log(xhr.status);
  432. console.log(thrownError);
  433. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  434. }
  435. });
  436. console.log('sent config JSON with headers:', JSON.stringify(headers));
  437. console.log('sent config JSON with data:', JSON.stringify(data));
  438. });
  439. $("#fwUpload").on("click", function() {
  440. var upload_path = "/flash.json";
  441. var fileInput = document.getElementById("flashfilename").files;
  442. if (fileInput.length == 0) {
  443. alert("No file selected!");
  444. } else {
  445. var file = fileInput[0];
  446. var xhttp = new XMLHttpRequest();
  447. xhttp.onreadystatechange = function() {
  448. if (xhttp.readyState == 4) {
  449. if (xhttp.status == 200) {
  450. showMessage(xhttp.responseText, 'MESSAGING_INFO')
  451. } else if (xhttp.status == 0) {
  452. showMessage("Upload connection was closed abruptly!", 'MESSAGING_ERROR');
  453. } else {
  454. showMessage(xhttp.status + " Error!\n" + xhttp.responseText, 'MESSAGING_ERROR');
  455. }
  456. }
  457. };
  458. xhttp.open("POST", upload_path, true);
  459. xhttp.send(file);
  460. }
  461. enableStatusTimer = true;
  462. });
  463. $("#flash").on("click", function() {
  464. var data = { 'timestamp': Date.now() };
  465. if (blockFlashButton) return;
  466. blockFlashButton = true;
  467. var url = $("#fwurl").val();
  468. data['config'] = {
  469. fwurl : {
  470. value : url,
  471. type : 33
  472. }
  473. };
  474. $.ajax({
  475. url: '/config.json',
  476. dataType: 'text',
  477. method: 'POST',
  478. cache: false,
  479. contentType: 'application/json; charset=utf-8',
  480. data: JSON.stringify(data),
  481. error: function (xhr, ajaxOptions, thrownError) {
  482. console.log(xhr.status);
  483. console.log(thrownError);
  484. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  485. }
  486. });
  487. enableStatusTimer = true;
  488. });
  489. $("#generate-command").on("click", function() {
  490. var commandLine = commandHeader + ' -n "' + $("#player").val() + '"';
  491. if (output == 'bt') {
  492. commandLine += ' -o "BT -n \'' + $("#btsink").val() + '\'" -R -Z 192000';
  493. } else if (output == 'spdif') {
  494. commandLine += ' -o SPDIF -R -Z 192000';
  495. } else {
  496. commandLine += ' -o I2S';
  497. }
  498. if ($("#optional").val() != '') {
  499. commandLine += ' ' + $("#optional").val();
  500. }
  501. $("#autoexec1").val(commandLine);
  502. });
  503. $('[name=audio]').on("click", function(){
  504. if (this.id == 'bt') {
  505. $("#btsinkdiv").show(200);
  506. output = 'bt';
  507. } else if (this.id == 'spdif') {
  508. $("#btsinkdiv").hide(200);
  509. output = 'spdif';
  510. } else {
  511. $("#btsinkdiv").hide(200);
  512. output = 'i2s';
  513. }
  514. });
  515. $('#fwcheck').on("click", function(){
  516. $("#releaseTable").html("");
  517. $.getJSON(releaseURL, function(data) {
  518. var i=0;
  519. var branches = [];
  520. data.forEach(function(release) {
  521. var [ver, idf, cfg, branch] = release.name.split('#');
  522. if (!branches.includes(branch)) {
  523. branches.push(branch);
  524. }
  525. });
  526. var fwb;
  527. branches.forEach(function(branch) {
  528. fwb += '<option value="' + branch + '">' + branch + '</option>';
  529. });
  530. $("#fwbranch").append(fwb);
  531. data.forEach(function(release) {
  532. var url = '';
  533. release.assets.forEach(function(asset) {
  534. if (asset.name.match(/\.bin$/)) {
  535. url = asset.browser_download_url;
  536. }
  537. });
  538. var [ver, idf, cfg, branch] = release.name.split('#');
  539. var body = release.body;
  540. body = body.replace(/\'/ig, "\"");
  541. body = body.replace(/[\s\S]+(### Revision Log[\s\S]+)### ESP-IDF Version Used[\s\S]+/, "$1");
  542. body = body.replace(/- \(.+?\) /g, "- ");
  543. var [date, time] = release.created_at.split('T');
  544. var trclass = (i++ > 6)?' hide':'';
  545. $("#releaseTable").append(
  546. "<tr class='release"+trclass+"'>"+
  547. "<td data-toggle='tooltip' title='"+body+"'>"+ver+"</td>"+
  548. "<td>"+date+"</td>"+
  549. "<td>"+cfg+"</td>"+
  550. "<td>"+idf+"</td>"+
  551. "<td>"+branch+"</td>"+
  552. "<td><input id='generate-command' type='button' class='btn btn-success' value='Select' data-url='"+url+"' onclick='setURL(this);' /></td>"+
  553. "</tr>"
  554. );
  555. });
  556. if (i > 7) {
  557. $("#releaseTable").append(
  558. "<tr id='showall'>"+
  559. "<td colspan='6'>"+
  560. "<input type='button' id='showallbutton' class='btn btn-info' value='Show older releases' />"+
  561. "</td>"+
  562. "</tr>"
  563. );
  564. $('#showallbutton').on("click", function(){
  565. $("tr.hide").removeClass("hide");
  566. $("tr#showall").addClass("hide");
  567. });
  568. }
  569. $("#searchfw").css("display", "inline");
  570. })
  571. .fail(function() {
  572. alert("failed to fetch release history!");
  573. });
  574. });
  575. $('input#searchinput').on("input", function(){
  576. var s = $('input#searchinput').val();
  577. var re = new RegExp(s, "gi");
  578. if (s.length == 0) {
  579. $("tr.release").removeClass("hide");
  580. } else if (s.length < 3) {
  581. $("tr.release").addClass("hide");
  582. } else {
  583. $("tr.release").addClass("hide");
  584. $("tr.release").each(function(tr){
  585. $(this).find('td').each (function() {
  586. if ($(this).html().match(re)) {
  587. $(this).parent().removeClass('hide');
  588. }
  589. });
  590. });
  591. }
  592. });
  593. $("#fwbranch").change(function(e) {
  594. var branch = this.value;
  595. var re = new RegExp('^'+branch+'$', "gi");
  596. $("tr.release").addClass("hide");
  597. $("tr.release").each(function(tr){
  598. $(this).find('td').each (function() {
  599. console.log($(this).html());
  600. if ($(this).html().match(re)) {
  601. $(this).parent().removeClass('hide');
  602. }
  603. });
  604. });
  605. });
  606. $('#boot-button').on("click", function(){
  607. enableStatusTimer = true;
  608. });
  609. $('#reboot-button').on("click", function(){
  610. enableStatusTimer = true;
  611. });
  612. $('#updateAP').on("click", function(){
  613. refreshAP(true);
  614. console.log("refresh AP");
  615. });
  616. //first time the page loads: attempt to get the connection status and start the wifi scan
  617. refreshAP(false);
  618. getConfig();
  619. getCommands();
  620. //start timers
  621. startCheckStatusInterval();
  622. //startRefreshAPInterval();
  623. $('[data-toggle="tooltip"]').tooltip({
  624. html: true,
  625. placement : 'right',
  626. });
  627. $('a[href^="#tab-firmware"]').dblclick(function () {
  628. dblclickCounter++;
  629. if(dblclickCounter>=2)
  630. {
  631. dblclickCounter=0;
  632. blockFlashButton=false;
  633. alert("Unocking flash button!");
  634. }
  635. });
  636. $('a[href^="#tab-firmware"]').click(function () {
  637. // when navigating back to this table, reset the counter
  638. if(!this.classList.contains("active")) dblclickCounter=0;
  639. });
  640. });
  641. function setURL(button) {
  642. var url = button.dataset.url;
  643. $("#fwurl").val(url);
  644. $('[data-url^="http"]').addClass("btn-success").removeClass("btn-danger");
  645. $('[data-url="'+url+'"]').addClass("btn-danger").removeClass("btn-success");
  646. }
  647. function performConnect(conntype){
  648. //stop the status refresh. This prevents a race condition where a status
  649. //request would be refreshed with wrong ip info from a previous connection
  650. //and the request would automatically shows as succesful.
  651. stopCheckStatusInterval();
  652. //stop refreshing wifi list
  653. stopRefreshAPInterval();
  654. var pwd;
  655. var dhcpname;
  656. if (conntype == 'manual') {
  657. //Grab the manual SSID and PWD
  658. selectedSSID=$('#manual_ssid').val();
  659. pwd = $("#manual_pwd").val();
  660. dhcpname= $("#dhcp-name2").val();;
  661. }else{
  662. pwd = $("#pwd").val();
  663. dhcpname= $("#dhcp-name1").val();;
  664. }
  665. //reset connection
  666. $( "#loading" ).show();
  667. $( "#connect-success" ).hide();
  668. $( "#connect-fail" ).hide();
  669. $( "#ok-connect" ).prop("disabled",true);
  670. $( "#ssid-wait" ).text(selectedSSID);
  671. $( "#connect" ).slideUp( "fast", function() {});
  672. $( "#connect_manual" ).slideUp( "fast", function() {});
  673. $( "#connect-wait" ).slideDown( "fast", function() {});
  674. $.ajax({
  675. url: '/connect.json',
  676. dataType: 'text',
  677. method: 'POST',
  678. cache: false,
  679. // headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd, 'X-Custom-host_name': dhcpname },
  680. contentType: 'application/json; charset=utf-8',
  681. data: JSON.stringify({ 'timestamp': Date.now(),
  682. 'ssid' : selectedSSID,
  683. 'pwd' : pwd,
  684. 'host_name' : dhcpname
  685. }),
  686. error: function (xhr, ajaxOptions, thrownError) {
  687. console.log(xhr.status);
  688. console.log(thrownError);
  689. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  690. }
  691. });
  692. //now we can re-set the intervals regardless of result
  693. startCheckStatusInterval();
  694. startRefreshAPInterval();
  695. }
  696. function rssiToIcon(rssi){
  697. if(rssi >= -60){
  698. return 'w0';
  699. }
  700. else if(rssi >= -67){
  701. return 'w1';
  702. }
  703. else if(rssi >= -75){
  704. return 'w2';
  705. }
  706. else{
  707. return 'w3';
  708. }
  709. }
  710. function refreshAP(force){
  711. if (!enableAPTimer && !force) return;
  712. $.getJSON( "/scan.json", async function( data ) {
  713. await sleep(2000);
  714. $.getJSON( "/ap.json", function( data ) {
  715. if(data.length > 0){
  716. //sort by signal strength
  717. data.sort(function (a, b) {
  718. var x = a["rssi"]; var y = b["rssi"];
  719. return ((x < y) ? 1 : ((x > y) ? -1 : 0));
  720. });
  721. apList = data;
  722. refreshAPHTML(apList);
  723. }
  724. });
  725. });
  726. }
  727. function refreshAPHTML(data){
  728. var h = "";
  729. data.forEach(function(e, idx, array) {
  730. h += '<div class="ape{0}"><div class="{1}"><div class="{2}">{3}</div></div></div>'.format(idx === array.length - 1?'':' brdb', rssiToIcon(e.rssi), e.auth==0?'':'pw',e.ssid);
  731. h += "\n";
  732. });
  733. $( "#wifi-list" ).html(h)
  734. }
  735. function getMessages() {
  736. $.getJSON("/messages.json?1", async function(data) {
  737. for (const msg of data) {
  738. var msg_age = msg["current_time"] - msg["sent_time"];
  739. var msg_time = new Date();
  740. msg_time.setTime( msg_time.getTime() - msg_age );
  741. switch (msg["class"]) {
  742. case "MESSAGING_CLASS_OTA":
  743. //message: "{"ota_dsc":"Erasing flash complete","ota_pct":0}"
  744. var ota_data = JSON.parse(msg["message"]);
  745. if (ota_data.hasOwnProperty('ota_pct') && ota_data['ota_pct'] != 0){
  746. otapct = ota_data['ota_pct'];
  747. $('.progress-bar').css('width', otapct+'%').attr('aria-valuenow', otapct);
  748. $('.progress-bar').html(otapct+'%');
  749. }
  750. if (ota_data.hasOwnProperty('ota_dsc') && ota_data['ota_dsc'] != ''){
  751. otadsc = ota_data['ota_dsc'];
  752. $("span#flash-status").html(otadsc);
  753. if (msg.type =="MESSAGING_ERROR" || otapct > 95) {
  754. blockFlashButton = false;
  755. enableStatusTimer = true;
  756. }
  757. }
  758. break;
  759. case "MESSAGING_CLASS_STATS":
  760. // for task states, check structure : task_state_t
  761. var stats_data = JSON.parse(msg["message"]);
  762. console.log(msg_time.toLocaleString() + " - Number of tasks on the ESP32: " + stats_data["ntasks"]);
  763. var stats_tasks = stats_data["tasks"];
  764. console.log(msg_time.toLocaleString() + '\tname' + '\tcpu' + '\tstate'+ '\tminstk'+ '\tbprio'+ '\tcprio'+ '\tnum' );
  765. stats_tasks.forEach(function(task) {
  766. console.log(msg_time.toLocaleString() + '\t' + task["nme"] + '\t'+ task["cpu"] + '\t'+ task_state_t[task["st"]]+ '\t'+ task["minstk"]+ '\t'+ task["bprio"]+ '\t'+ task["cprio"]+ '\t'+ task["num"]);
  767. });
  768. break;
  769. case "MESSAGING_CLASS_SYSTEM":
  770. var r = await showMessage(msg["message"], msg["type"],msg_age);
  771. $("#syslogTable").append(
  772. "<tr class='"+msg["type"]+"'>"+
  773. "<td>"+msg_time.toLocaleString()+"</td>"+
  774. "<td>"+escapeHTML(msg["message"]).replace(/\n/g, '<br />')+"</td>"+
  775. "</tr>"
  776. );
  777. break;
  778. default:
  779. break;
  780. }
  781. }
  782. })
  783. .fail(function(xhr, ajaxOptions, thrownError) {
  784. console.log(xhr.status);
  785. console.log(thrownError);
  786. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  787. });
  788. /*
  789. Minstk is minimum stack space left
  790. Bprio is base priority
  791. cprio is current priority
  792. nme is name
  793. st is task state. I provided a "typedef" that you can use to convert to text
  794. cpu is cpu percent used
  795. */
  796. }
  797. function checkStatus(){
  798. RepeatCheckStatusInterval();
  799. if (!enableStatusTimer) return;
  800. if (blockAjax) return;
  801. blockAjax = true;
  802. getMessages();
  803. $.getJSON( "/status.json", function( data ) {
  804. if (data.hasOwnProperty('ssid') && data['ssid'] != ""){
  805. if (data["ssid"] === selectedSSID){
  806. //that's a connection attempt
  807. if (data["urc"] === 0){
  808. //got connection
  809. $("#connected-to span").text(data["ssid"]);
  810. $("#connect-details h1").text(data["ssid"]);
  811. $("#ip").text(data["ip"]);
  812. $("#netmask").text(data["netmask"]);
  813. $("#gw").text(data["gw"]);
  814. $("#wifi-status").slideDown( "fast", function() {});
  815. $("span#foot-wifi").html(", SSID: <strong>"+data["ssid"]+"</strong>, IP: <strong>"+data["ip"]+"</strong>");
  816. //unlock the wait screen if needed
  817. $( "#ok-connect" ).prop("disabled",false);
  818. //update wait screen
  819. $( "#loading" ).hide();
  820. $( "#connect-success" ).text("Your IP address now is: " + data["ip"] );
  821. $( "#connect-success" ).show();
  822. $( "#connect-fail" ).hide();
  823. enableAPTimer = false;
  824. if (!recovery) enableStatusTimer = false;
  825. }
  826. else if (data["urc"] === 1){
  827. //failed attempt
  828. $("#connected-to span").text('');
  829. $("#connect-details h1").text('');
  830. $("#ip").text('0.0.0.0');
  831. $("#netmask").text('0.0.0.0');
  832. $("#gw").text('0.0.0.0');
  833. $("span#foot-wifi").html("");
  834. //don't show any connection
  835. $("#wifi-status").slideUp( "fast", function() {});
  836. //unlock the wait screen
  837. $( "#ok-connect" ).prop("disabled",false);
  838. //update wait screen
  839. $( "#loading" ).hide();
  840. $( "#connect-fail" ).show();
  841. $( "#connect-success" ).hide();
  842. enableAPTimer = true;
  843. enableStatusTimer = true;
  844. }
  845. }
  846. else if (data.hasOwnProperty('urc') && data['urc'] === 0){
  847. //ESP32 is already connected to a wifi without having the user do anything
  848. if( !($("#wifi-status").is(":visible")) ){
  849. $("#connected-to span").text(data["ssid"]);
  850. $("#connect-details h1").text(data["ssid"]);
  851. $("#ip").text(data["ip"]);
  852. $("#netmask").text(data["netmask"]);
  853. $("#gw").text(data["gw"]);
  854. $("#wifi-status").slideDown( "fast", function() {});
  855. $("span#foot-wifi").html(", SSID: <strong>"+data["ssid"]+"</strong>, IP: <strong>"+data["ip"]+"</strong>");
  856. }
  857. enableAPTimer = false;
  858. if (!recovery) enableStatusTimer = false;
  859. }
  860. }
  861. else if (data.hasOwnProperty('urc') && data['urc'] === 2){
  862. //that's a manual disconnect
  863. if($("#wifi-status").is(":visible")){
  864. $("#wifi-status").slideUp( "fast", function() {});
  865. $("span#foot-wifi").html("");
  866. }
  867. enableAPTimer = true;
  868. enableStatusTimer = true;
  869. }
  870. if (data.hasOwnProperty('recovery')) {
  871. if(LastRecoveryState != data["recovery"]){
  872. LastRecoveryState = data["recovery"];
  873. $("input#show-nvs")[0].checked=LastRecoveryState==1?true:false;
  874. }
  875. if($("input#show-nvs")[0].checked){
  876. $('a[href^="#tab-nvs"]').show();
  877. } else{
  878. $('a[href^="#tab-nvs"]').hide();
  879. }
  880. if (data["recovery"] === 1) {
  881. recovery = true;
  882. $("#otadiv").show();
  883. $('a[href^="#tab-audio"]').hide();
  884. $('a[href^="#tab-gpio"]').show();
  885. $('#uploaddiv').show();
  886. $("footer.footer").removeClass('sl');
  887. $("footer.footer").addClass('recovery');
  888. $("#boot-button").html('Reboot');
  889. $("#boot-form").attr('action', '/reboot_ota.json');
  890. enableStatusTimer = true;
  891. } else {
  892. recovery = false;
  893. $("#otadiv").hide();
  894. $('a[href^="#tab-audio"]').show();
  895. $('a[href^="#tab-gpio"]').hide();
  896. $('#uploaddiv').hide();
  897. $("footer.footer").removeClass('recovery');
  898. $("footer.footer").addClass('sl');
  899. $("#boot-button").html('Recovery');
  900. $("#boot-form").attr('action', '/recovery.json');
  901. enableStatusTimer = false;
  902. }
  903. }
  904. if (data.hasOwnProperty('project_name') && data['project_name'] != ''){
  905. pname = data['project_name'];
  906. }
  907. if (data.hasOwnProperty('version') && data['version'] != ''){
  908. ver = data['version'];
  909. $("span#foot-fw").html("fw: <strong>"+ver+"</strong>, mode: <strong>"+pname+"</strong>");
  910. }
  911. else {
  912. $("span#flash-status").html('');
  913. }
  914. if (data.hasOwnProperty('Voltage')) {
  915. var voltage = data['Voltage'];
  916. var layer;
  917. if (voltage > 0) {
  918. if (inRange(voltage, 5.8, 6.2) || inRange(voltage, 8.8, 9.2)) {
  919. layer = bat0;
  920. } else if (inRange(voltage, 6.2, 6.8) || inRange(voltage, 9.2, 10.0)) {
  921. layer = bat1;
  922. } else if (inRange(voltage, 6.8, 7.1) || inRange(voltage, 10.0, 10.5)) {
  923. layer = bat2;
  924. } else if (inRange(voltage, 7.1, 7.5) || inRange(voltage, 10.5, 11.0)) {
  925. layer = bat3;
  926. } else {
  927. layer = bat4;
  928. }
  929. layer.setAttribute("display","inline");
  930. }
  931. }
  932. if (data.hasOwnProperty('Jack')) {
  933. var jack = data['Jack'];
  934. if (jack == '1') {
  935. o_jack.setAttribute("display","inline");
  936. }
  937. }
  938. blockAjax = false;
  939. })
  940. .fail(function(xhr, ajaxOptions, thrownError) {
  941. console.log(xhr.status);
  942. console.log(thrownError);
  943. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  944. blockAjax = false;
  945. });
  946. }
  947. function runCommand(button,reboot) {
  948. cmdstring = button.attributes.cmdname.value;
  949. fields=document.getElementById("flds-"+cmdstring);
  950. cmdstring+=' ';
  951. if(fields){
  952. allfields=fields.querySelectorAll("select,input");
  953. for (i = 0; i < allfields.length; i++) {
  954. attr=allfields[i].attributes;
  955. qts='';
  956. opt='';
  957. optspacer=' ';
  958. isSelect=allfields[i].attributes?.class?.value=="custom-select";
  959. if(( isSelect && allfields[i].selectedIndex != 0 )|| !isSelect ){
  960. if (attr.longopts.value!== "undefined"){
  961. opt+= '--' + attr.longopts.value;
  962. optspacer='=';
  963. }
  964. else if(attr.shortopts.value!== "undefined"){
  965. opt= '-' + attr.shortopts.value;
  966. }
  967. if(attr.hasvalue.value== "true" ){
  968. if(allfields[i].value!=''){
  969. qts = (/\s/.test(allfields[i].value))?'"':'';
  970. cmdstring+=opt+optspacer+qts +allfields[i].value +qts+ ' ';
  971. }
  972. }
  973. else {
  974. // this is a checkbox
  975. if(allfields[i].checked) cmdstring+=opt+ ' ';
  976. }
  977. }
  978. }
  979. }
  980. console.log(cmdstring);
  981. var data = { 'timestamp': Date.now() };
  982. data['command'] = cmdstring;
  983. $.ajax({
  984. url: '/commands.json',
  985. dataType: 'text',
  986. method: 'POST',
  987. cache: false,
  988. contentType: 'application/json; charset=utf-8',
  989. data: JSON.stringify(data),
  990. error: function (xhr, ajaxOptions, thrownError) {
  991. console.log(xhr.status);
  992. console.log(thrownError);
  993. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  994. },
  995. complete: function(response) {
  996. //var returnedResponse = JSON.parse(response.responseText);
  997. console.log(response.responseText);
  998. if(reboot){
  999. showMessage('Applying. Please wait for the ESP32 to reboot', 'MESSAGING_WARNING');
  1000. console.log('now triggering reboot');
  1001. $.ajax({
  1002. url: '/reboot.json',
  1003. dataType: 'text',
  1004. method: 'POST',
  1005. cache: false,
  1006. contentType: 'application/json; charset=utf-8',
  1007. data: JSON.stringify({ 'timestamp': Date.now()}),
  1008. error: function (xhr, ajaxOptions, thrownError) {
  1009. console.log(xhr.status);
  1010. console.log(thrownError);
  1011. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  1012. },
  1013. complete: function(response) {
  1014. console.log('reboot call completed');
  1015. Promise.resolve().delay(5000).then(function(v) {
  1016. console.log('Getting updated commands');
  1017. getCommands();
  1018. });
  1019. }
  1020. });
  1021. }
  1022. }
  1023. });
  1024. enableStatusTimer = true;
  1025. }
  1026. function getCommands() {
  1027. $.getJSON("/commands.json", function(data) {
  1028. console.log(data);
  1029. data.commands.forEach(function(command) {
  1030. if($("#flds-"+command.name).length == 0){
  1031. isConfig=($('#'+command.name+'-list').length>0);
  1032. innerhtml='';
  1033. //innerhtml+='<tr class="table-light"><td>'+(isConfig?'<h1>':'');
  1034. innerhtml+='<fieldset id="flds-'+command.name+'"><legend>' + escapeHTML(command.help).replace(/\n/g, '<br />')+'</legend>';
  1035. if(command.hasOwnProperty("argtable")){
  1036. command.argtable.forEach(function (arg){
  1037. placeholder=arg?.datatype || '';
  1038. ctrlname=command.name+'-'+arg.longopts;
  1039. curvalue=data.values?.[command.name]?.[arg.longopts] || '';
  1040. var attributes='hasvalue='+arg.hasvalue+' ';
  1041. //attributes +='datatype="'+arg.datatype+'" ';
  1042. attributes+='longopts="'+arg.longopts+'" ';
  1043. attributes+='shortopts="'+arg.shortopts+'" ';
  1044. attributes+='checkbox='+arg.checkbox+' ';
  1045. attributes+='cmdname="'+command.name+'" ';
  1046. attributes+= 'id="'+ctrlname+'" name="'+ctrlname+'" hasvalue="'+arg.hasvalue+'" ';
  1047. if(arg.checkbox){
  1048. innerhtml+='<div class="form-check"><label class="form-check-label">';
  1049. innerhtml+='<input type="checkbox" '+attributes+' class="form-check-input" value="" >'+arg.glossary+'</label>';
  1050. }
  1051. else {
  1052. innerhtml+='<div class="form-group" ><label for="'+ctrlname+'">'+ arg.glossary+'</label>';
  1053. if(placeholder.includes('|')){
  1054. placeholder = placeholder.replace('<','').replace('>','');
  1055. innerhtml+='<select '+attributes + ' class="form-control"';
  1056. placeholder='--|'+placeholder;
  1057. placeholder.split('|').forEach(function(choice){
  1058. innerhtml+='<option >'+choice+'</option>';
  1059. });
  1060. innerhtml+='</select>';
  1061. }
  1062. else {
  1063. innerhtml+='<input type="text" class="form-control" placeholder="'+placeholder+'" '+attributes+'>';
  1064. }
  1065. innerhtml+='<small class="form-text text-muted">Previous value: '+curvalue+'</small>';
  1066. }
  1067. innerhtml+='</div>';
  1068. });
  1069. }
  1070. if(isConfig){
  1071. innerhtml+='<button type="submit" class="btn btn-primary" id="btn-'+ command.name +'" cmdname="'+command.name+'" onclick="runCommand(this,false)">Save</button>';
  1072. innerhtml+='<button type="submit" class="btn btn-primary" id="btn-'+ command.name +'" cmdname="'+command.name+'" onclick="runCommand(this,true)">Apply</button>';
  1073. }
  1074. else {
  1075. innerhtml+='<button type="submit" class="btn btn-primary" id="btn-'+ command.name +'" cmdname="'+command.name+'" onclick="runCommand(this,false)">Execute</button>';
  1076. }
  1077. innerhtml+='</fieldset>';
  1078. if(isConfig){
  1079. $('#'+command.name+'-list').append(innerhtml);
  1080. }
  1081. else {
  1082. $("#commands-list").append(innerhtml);
  1083. }
  1084. }
  1085. });
  1086. data.commands.forEach(function(command) {
  1087. if(command.hasOwnProperty("argtable")){
  1088. command.argtable.forEach(function (arg){
  1089. ctrlselector='#'+command.name+'-'+arg.longopts;
  1090. if(arg.checkbox){
  1091. $(ctrlselector)[0].checked=data.values?.[command.name]?.[arg.longopts];
  1092. }
  1093. else {
  1094. $(ctrlselector)[0].value=data.values?.[command.name]?.[arg.longopts] || '';
  1095. if($(ctrlselector)[0].value.length==0 && (arg?.datatype || '').includes('|')){
  1096. $(ctrlselector)[0].value='--';
  1097. }
  1098. }
  1099. });
  1100. }
  1101. });
  1102. })
  1103. .fail(function(xhr, ajaxOptions, thrownError) {
  1104. console.log(xhr.status);
  1105. console.log(thrownError);
  1106. $("#commands-list").empty();
  1107. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  1108. blockAjax = false;
  1109. });
  1110. }
  1111. function getConfig() {
  1112. $.getJSON("/config.json", function(data) {
  1113. Object.keys(data).sort().forEach(function(key, i) {
  1114. if (data.hasOwnProperty(key)) {
  1115. if (key == 'autoexec') {
  1116. if (data["autoexec"].value === "1") {
  1117. $("#autoexec-cb")[0].checked=true;
  1118. } else {
  1119. $("#autoexec-cb")[0].checked=false;
  1120. }
  1121. } else if (key == 'autoexec1') {
  1122. $("textarea#autoexec1").val(data[key].value);
  1123. var re = / -o "?(\S+)\b/g;
  1124. var m = re.exec(data[key].value);
  1125. if (m[1] =='I2S') {
  1126. o_i2s.setAttribute("display","inline");
  1127. } else if (m[1] =='SPDIF') {
  1128. o_spdif.setAttribute("display","inline");
  1129. } else if (m[1] =='BT') {
  1130. o_bt.setAttribute("display","inline");
  1131. }
  1132. } else if (key == 'host_name') {
  1133. $("input#dhcp-name1").val(data[key].value);
  1134. $("input#dhcp-name2").val(data[key].value);
  1135. }
  1136. $("tbody#nvsTable").append(
  1137. "<tr>"+
  1138. "<td>"+key+"</td>"+
  1139. "<td class='value'>"+
  1140. "<input type='text' class='form-control nvs' id='"+key+"' nvs_type="+data[key].type+" >"+
  1141. "</td>"+
  1142. "</tr>"
  1143. );
  1144. $("input#"+key).val(data[key].value);
  1145. }
  1146. });
  1147. $("tbody#nvsTable").append(
  1148. "<tr>"+
  1149. "<td>"+
  1150. "<input type='text' class='form-control' id='nvs-new-key' placeholder='new key'>"+
  1151. "</td>"+
  1152. "<td>"+
  1153. "<input type='text' class='form-control' id='nvs-new-value' placeholder='new value' nvs_type=33 >"+ // todo: provide a way to choose field type
  1154. "</td>"+
  1155. "</tr>"
  1156. );
  1157. })
  1158. .fail(function(xhr, ajaxOptions, thrownError) {
  1159. console.log(xhr.status);
  1160. console.log(thrownError);
  1161. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  1162. blockAjax = false;
  1163. });
  1164. }
  1165. function showMessage(message, severity, age=0) {
  1166. if (severity == 'MESSAGING_INFO') {
  1167. $('#message').css('background', '#6af');
  1168. } else if (severity == 'MESSAGING_WARNING') {
  1169. $('#message').css('background', '#ff0');
  1170. } else if (severity == 'MESSAGING_ERROR' ) {
  1171. $('#message').css('background', '#f00');
  1172. } else {
  1173. $('#message').css('background', '#f00');
  1174. }
  1175. $('#message').html(message);
  1176. return new Promise(function(resolve, reject) {
  1177. $("#content").fadeTo("slow", 0.3, function() {
  1178. $("#message").show(500).delay(5000).hide(500, function() {
  1179. $("#content").fadeTo("slow", 1.0, function() {
  1180. resolve(true);
  1181. });
  1182. });
  1183. });
  1184. });
  1185. }
  1186. function inRange(x, min, max) {
  1187. return ((x-min)*(x-max) <= 0);
  1188. }
  1189. function sleep(ms) {
  1190. return new Promise(resolve => setTimeout(resolve, ms));
  1191. }