code.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311
  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. pardiv = button.parentNode.parentNode;
  949. cmdstring = button.attributes.cmdname.value;
  950. fields=document.getElementById("flds-"+cmdstring);
  951. cmdstring+=' ';
  952. if(fields){
  953. hint = pardiv.hint;
  954. allfields=fields.querySelectorAll("select,input");
  955. for (i = 0; i < allfields.length; i++) {
  956. attr=allfields[i].attributes;
  957. qts='';
  958. opt='';
  959. optspacer=' ';
  960. isSelect=allfields[i].attributes?.class?.value=="custom-select";
  961. if(( isSelect && allfields[i].selectedIndex != 0 )|| !isSelect ){
  962. if (attr.longopts.value!== "undefined"){
  963. opt+= '--' + attr.longopts.value;
  964. optspacer='=';
  965. }
  966. else if(attr.shortopts.value!== "undefined"){
  967. opt= '-' + attr.shortopts.value;
  968. }
  969. if(attr.hasvalue.value== "true" ){
  970. if(allfields[i].value!=''){
  971. qts = (/\s/.test(allfields[i].value))?'"':'';
  972. cmdstring+=opt+optspacer+qts +allfields[i].value +qts+ ' ';
  973. }
  974. }
  975. else {
  976. // this is a checkbox
  977. if(allfields[i].checked) cmdstring+=opt+ ' ';
  978. }
  979. }
  980. }
  981. }
  982. console.log(cmdstring);
  983. var data = { 'timestamp': Date.now() };
  984. data['command'] = cmdstring;
  985. $.ajax({
  986. url: '/commands.json',
  987. dataType: 'text',
  988. method: 'POST',
  989. cache: false,
  990. contentType: 'application/json; charset=utf-8',
  991. data: JSON.stringify(data),
  992. error: function (xhr, ajaxOptions, thrownError) {
  993. console.log(xhr.status);
  994. console.log(thrownError);
  995. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  996. },
  997. complete: function(response) {
  998. //var returnedResponse = JSON.parse(response.responseText);
  999. console.log(response.responseText);
  1000. if(reboot){
  1001. showMessage('Applying. Please wait for the ESP32 to reboot', 'MESSAGING_WARNING');
  1002. console.log('now triggering reboot');
  1003. $.ajax({
  1004. url: '/reboot.json',
  1005. dataType: 'text',
  1006. method: 'POST',
  1007. cache: false,
  1008. contentType: 'application/json; charset=utf-8',
  1009. data: JSON.stringify({ 'timestamp': Date.now()}),
  1010. error: function (xhr, ajaxOptions, thrownError) {
  1011. console.log(xhr.status);
  1012. console.log(thrownError);
  1013. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  1014. },
  1015. complete: function(response) {
  1016. console.log('reboot call completed');
  1017. Promise.resolve().delay(5000).then(function(v) {
  1018. console.log('Getting updated commands');
  1019. getCommands();
  1020. });
  1021. }
  1022. });
  1023. }
  1024. }
  1025. });
  1026. enableStatusTimer = true;
  1027. }
  1028. function getCommands() {
  1029. $.getJSON("/commands.json", function(data) {
  1030. console.log(data);
  1031. var advancedtabhtml='';
  1032. data.commands.forEach(function(command) {
  1033. if($("#flds-"+command.name).length == 0){
  1034. isConfig=($('#'+command.name+'-list').length>0);
  1035. innerhtml='';
  1036. innerhtml+='<tr><td>'+(isConfig?'<h1>':'');
  1037. innerhtml+=escapeHTML(command.help).replace(/\n/g, '<br />')+(isConfig?'</h1>':'<br>');
  1038. innerhtml+='<div >';
  1039. if(command.hasOwnProperty("argtable")){
  1040. innerhtml+='<table class="table table-hover" id="flds-'+command.name+'"><tbody>';
  1041. command.argtable.forEach(function (arg){
  1042. placeholder=arg?.datatype || '';
  1043. ctrlname=command.name+'-'+arg.longopts;
  1044. curvalue=data.values?.[command.name]?.[arg.longopts] || '';
  1045. innerhtml+="<tr>";
  1046. var attributes ='datatype="'+arg.datatype+'" ';
  1047. attributes+='hasvalue='+arg.hasvalue+' ';
  1048. attributes+='longopts="'+arg.longopts+'" ';
  1049. attributes+='shortopts="'+arg.shortopts+'" ';
  1050. attributes+='checkbox='+arg.checkbox+' ';
  1051. attributes+='cmdname="'+command.name+'" ';
  1052. attributes+= 'id="'+ctrlname+'" name="'+ctrlname+'" placeholder="'+placeholder+'" hasvalue="'+arg.hasvalue+'" ';
  1053. if(placeholder.includes('|')){
  1054. placeholder = placeholder.replace('<','').replace('>','');
  1055. innerhtml+='<td><select ';
  1056. innerhtml+=attributes;
  1057. innerhtml+=' class="custom-select">';
  1058. innerhtml+='<option '+(curvalue.length>0?'value':'selected')+'>'+arg.glossary+'</option>'
  1059. placeholder.split('|').forEach(function(choice){
  1060. innerhtml+='<option '+(curvalue.length>0&&curvalue==choice?'selected':'value')+'="'+choice+'">'+choice+'</option>';
  1061. });
  1062. innerhtml+='</select></td>';
  1063. }
  1064. else {
  1065. ctrltype="text";
  1066. if(arg.checkbox){
  1067. ctrltype="checkbox";
  1068. }
  1069. innerhtml+='<td><label for="'+ctrlname+'">'+ arg.glossary+'</label></td>';
  1070. innerhtml+='<td><input type="'+ctrltype+'"';
  1071. innerhtml+=attributes;
  1072. if(arg.checkbox){
  1073. if(data.values?.[command.name]?.[arg.longopts] ){
  1074. innerhtml+='checked ';
  1075. }
  1076. innerhtml+='></input></td>';
  1077. }
  1078. else {
  1079. innerhtml+='value="'+curvalue+'" ';
  1080. innerhtml+='></input></td>'+ curvalue.length>0?'<td>last: '+curvalue+'</td>':'';
  1081. }
  1082. }
  1083. innerhtml+="</tr>";
  1084. });
  1085. innerhtml+='</tbody></table>';
  1086. }
  1087. if(isConfig){
  1088. innerhtml+='<div class="buttons"><input id="btn-'+ command.name + '" type="button" class="btn btn-success" cmdname="'+command.name+'" value="Save" onclick="runCommand(this,false);">';
  1089. innerhtml+='<input id="btn-'+ command.name + '-apply" type="button" class="btn btn-success" cmdname="'+command.name+'" value="Apply" onclick="runCommand(this,true);"></div></div><td></tr>';
  1090. $('#'+command.name+'-list').append(innerhtml);
  1091. }
  1092. else {
  1093. advancedtabhtml+='<br>'+innerhtml;
  1094. advancedtabhtml+='<div class="buttons"><input id="btn-'+ command.name + '" type="button" class="btn btn-danger btn-sm" cmdname="'+command.name+'" value="'+command.name+'" onclick="runCommand(this, false);"></div></div><td></tr>';
  1095. }
  1096. }
  1097. });
  1098. $("#commands-list").append(advancedtabhtml);
  1099. data.commands.forEach(function(command) {
  1100. if(command.hasOwnProperty("argtable")){
  1101. command.argtable.forEach(function (arg){
  1102. ctrlselector='#'+command.name+'-'+arg.longopts;
  1103. if(arg.checkbox){
  1104. $(ctrlselector)[0].checked=data.values?.[command.name]?.[arg.longopts];
  1105. }
  1106. else {
  1107. $(ctrlselector)[0].value=data.values?.[command.name]?.[arg.longopts] || '';
  1108. }
  1109. });
  1110. }
  1111. });
  1112. })
  1113. .fail(function(xhr, ajaxOptions, thrownError) {
  1114. console.log(xhr.status);
  1115. console.log(thrownError);
  1116. $("#commands-list").empty();
  1117. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  1118. blockAjax = false;
  1119. });
  1120. }
  1121. function getConfig() {
  1122. $.getJSON("/config.json", function(data) {
  1123. Object.keys(data).sort().forEach(function(key, i) {
  1124. if (data.hasOwnProperty(key)) {
  1125. if (key == 'autoexec') {
  1126. if (data["autoexec"].value === "1") {
  1127. $("#autoexec-cb")[0].checked=true;
  1128. } else {
  1129. $("#autoexec-cb")[0].checked=false;
  1130. }
  1131. } else if (key == 'autoexec1') {
  1132. $("textarea#autoexec1").val(data[key].value);
  1133. var re = / -o "?(\S+)\b/g;
  1134. var m = re.exec(data[key].value);
  1135. if (m[1] =='I2S') {
  1136. o_i2s.setAttribute("display","inline");
  1137. } else if (m[1] =='SPDIF') {
  1138. o_spdif.setAttribute("display","inline");
  1139. } else if (m[1] =='BT') {
  1140. o_bt.setAttribute("display","inline");
  1141. }
  1142. } else if (key == 'host_name') {
  1143. $("input#dhcp-name1").val(data[key].value);
  1144. $("input#dhcp-name2").val(data[key].value);
  1145. }
  1146. $("tbody#nvsTable").append(
  1147. "<tr>"+
  1148. "<td>"+key+"</td>"+
  1149. "<td class='value'>"+
  1150. "<input type='text' class='form-control nvs' id='"+key+"' nvs_type="+data[key].type+" >"+
  1151. "</td>"+
  1152. "</tr>"
  1153. );
  1154. $("input#"+key).val(data[key].value);
  1155. }
  1156. });
  1157. $("tbody#nvsTable").append(
  1158. "<tr>"+
  1159. "<td>"+
  1160. "<input type='text' class='form-control' id='nvs-new-key' placeholder='new key'>"+
  1161. "</td>"+
  1162. "<td>"+
  1163. "<input type='text' class='form-control' id='nvs-new-value' placeholder='new value' nvs_type=33 >"+ // todo: provide a way to choose field type
  1164. "</td>"+
  1165. "</tr>"
  1166. );
  1167. })
  1168. .fail(function(xhr, ajaxOptions, thrownError) {
  1169. console.log(xhr.status);
  1170. console.log(thrownError);
  1171. if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
  1172. blockAjax = false;
  1173. });
  1174. }
  1175. function showMessage(message, severity, age=0) {
  1176. if (severity == 'MESSAGING_INFO') {
  1177. $('#message').css('background', '#6af');
  1178. } else if (severity == 'MESSAGING_WARNING') {
  1179. $('#message').css('background', '#ff0');
  1180. } else if (severity == 'MESSAGING_ERROR' ) {
  1181. $('#message').css('background', '#f00');
  1182. } else {
  1183. $('#message').css('background', '#f00');
  1184. }
  1185. $('#message').html(message);
  1186. return new Promise(function(resolve, reject) {
  1187. $("#content").fadeTo("slow", 0.3, function() {
  1188. $("#message").show(500).delay(5000).hide(500, function() {
  1189. $("#content").fadeTo("slow", 1.0, function() {
  1190. resolve(true);
  1191. });
  1192. });
  1193. });
  1194. });
  1195. }
  1196. function inRange(x, min, max) {
  1197. return ((x-min)*(x-max) <= 0);
  1198. }
  1199. function sleep(ms) {
  1200. return new Promise(resolve => setTimeout(resolve, ms));
  1201. }