code.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  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 releaseURL = 'https://api.github.com/repos/sle118/squeezelite-esp32/releases';
  14. var recovery = false;
  15. var enableAPTimer = true;
  16. var enableStatusTimer = true;
  17. var commandHeader = 'squeezelite -b 500:2000 -d all=info -C 30 -W';
  18. var pname, ver, otapct, otadsc;
  19. var blockAjax = false;
  20. var blockFlashButton = false;
  21. var lastMsg = '';
  22. var apList = null;
  23. var selectedSSID = "";
  24. var refreshAPInterval = null;
  25. var checkStatusInterval = null;
  26. var StatusIntervalActive = false;
  27. var RefreshAPIIntervalActive = false;
  28. var LastRecoveryState=null;
  29. var output = '';
  30. function stopCheckStatusInterval(){
  31. if(checkStatusInterval != null){
  32. clearTimeout(checkStatusInterval);
  33. checkStatusInterval = null;
  34. }
  35. StatusIntervalActive = false;
  36. }
  37. function stopRefreshAPInterval(){
  38. if(refreshAPInterval != null){
  39. clearTimeout(refreshAPInterval);
  40. refreshAPInterval = null;
  41. }
  42. RefreshAPIIntervalActive = false;
  43. }
  44. function startCheckStatusInterval(){
  45. StatusIntervalActive = true;
  46. checkStatusInterval = setTimeout(checkStatus, 3000);
  47. }
  48. function startRefreshAPInterval(){
  49. RefreshAPIIntervalActive = true;
  50. refreshAPInterval = setTimeout(refreshAP(false), 4500); // leave enough time for the initial scan
  51. }
  52. function RepeatCheckStatusInterval(){
  53. if(StatusIntervalActive)
  54. startCheckStatusInterval();
  55. }
  56. function RepeatRefreshAPInterval(){
  57. if(RefreshAPIIntervalActive)
  58. startRefreshAPInterval();
  59. }
  60. $(document).ready(function(){
  61. $("#wifi-status").on("click", ".ape", function() {
  62. $( "#wifi" ).slideUp( "fast", function() {});
  63. $( "#connect-details" ).slideDown( "fast", function() {});
  64. });
  65. $("#manual_add").on("click", ".ape", function() {
  66. selectedSSID = $(this).text();
  67. $( "#ssid-pwd" ).text(selectedSSID);
  68. $( "#wifi" ).slideUp( "fast", function() {});
  69. $( "#connect_manual" ).slideDown( "fast", function() {});
  70. $( "#connect" ).slideUp( "fast", function() {});
  71. //update wait screen
  72. $( "#loading" ).show();
  73. $( "#connect-success" ).hide();
  74. $( "#connect-fail" ).hide();
  75. });
  76. $("#wifi-list").on("click", ".ape", function() {
  77. selectedSSID = $(this).text();
  78. $( "#ssid-pwd" ).text(selectedSSID);
  79. $( "#wifi" ).slideUp( "fast", function() {});
  80. $( "#connect_manual" ).slideUp( "fast", function() {});
  81. $( "#connect" ).slideDown( "fast", function() {});
  82. //update wait screen
  83. $( "#loading" ).show();
  84. $( "#connect-success" ).hide();
  85. $( "#connect-fail" ).hide();
  86. });
  87. $("#cancel").on("click", function() {
  88. selectedSSID = "";
  89. $( "#connect" ).slideUp( "fast", function() {});
  90. $( "#connect_manual" ).slideUp( "fast", function() {});
  91. $( "#wifi" ).slideDown( "fast", function() {});
  92. });
  93. $("#manual_cancel").on("click", function() {
  94. selectedSSID = "";
  95. $( "#connect" ).slideUp( "fast", function() {});
  96. $( "#connect_manual" ).slideUp( "fast", function() {});
  97. $( "#wifi" ).slideDown( "fast", function() {});
  98. });
  99. $("#join").on("click", function() {
  100. performConnect();
  101. });
  102. $("#manual_join").on("click", function() {
  103. performConnect($(this).data('connect'));
  104. });
  105. $("#ok-details").on("click", function() {
  106. $( "#connect-details" ).slideUp( "fast", function() {});
  107. $( "#wifi" ).slideDown( "fast", function() {});
  108. });
  109. $("#ok-credits").on("click", function() {
  110. $( "#credits" ).slideUp( "fast", function() {});
  111. $( "#app" ).slideDown( "fast", function() {});
  112. });
  113. $("#acredits").on("click", function(event) {
  114. event.preventDefault();
  115. $( "#app" ).slideUp( "fast", function() {});
  116. $( "#credits" ).slideDown( "fast", function() {});
  117. });
  118. $("#ok-connect").on("click", function() {
  119. $( "#connect-wait" ).slideUp( "fast", function() {});
  120. $( "#wifi" ).slideDown( "fast", function() {});
  121. });
  122. $("#disconnect").on("click", function() {
  123. $( "#connect-details-wrap" ).addClass('blur');
  124. $( "#diag-disconnect" ).slideDown( "fast", function() {});
  125. });
  126. $("#no-disconnect").on("click", function() {
  127. $( "#diag-disconnect" ).slideUp( "fast", function() {});
  128. $( "#connect-details-wrap" ).removeClass('blur');
  129. });
  130. $("#yes-disconnect").on("click", function() {
  131. stopCheckStatusInterval();
  132. selectedSSID = "";
  133. $( "#diag-disconnect" ).slideUp( "fast", function() {});
  134. $( "#connect-details-wrap" ).removeClass('blur');
  135. $.ajax({
  136. url: '/connect.json',
  137. dataType: 'text',
  138. method: 'DELETE',
  139. cache: false,
  140. contentType: 'application/json; charset=utf-8',
  141. data: JSON.stringify({ 'timestamp': Date.now()})
  142. });
  143. startCheckStatusInterval();
  144. $( "#connect-details" ).slideUp( "fast", function() {});
  145. $( "#wifi" ).slideDown( "fast", function() {})
  146. });
  147. $("input#show-nvs").on("click", function() {
  148. this.checked=this.checked?1:0;
  149. if(this.checked){
  150. $('a[href^="#tab-nvs"]').show();
  151. } else {
  152. $('a[href^="#tab-nvs"]').hide();
  153. }
  154. });
  155. $("input#autoexec-cb").on("click", function() {
  156. var data = { 'timestamp': Date.now() };
  157. autoexec = (this.checked)?1:0;
  158. data['autoexec'] = autoexec;
  159. showMessage('please wait for the ESP32 to reboot', 'WARNING');
  160. $.ajax({
  161. url: '/config.json',
  162. dataType: 'text',
  163. method: 'POST',
  164. cache: false,
  165. headers: { "X-Custom-autoexec": autoexec },
  166. contentType: 'application/json; charset=utf-8',
  167. data: JSON.stringify(data),
  168. error: function (xhr, ajaxOptions, thrownError) {
  169. console.log(xhr.status);
  170. console.log(thrownError);
  171. if (thrownError != '') showMessage(thrownError, 'ERROR');
  172. },
  173. complete: function(response) {
  174. //var returnedResponse = JSON.parse(response.responseText);
  175. console.log(response.responseText);
  176. console.log('sent config JSON with headers:', autoexec);
  177. console.log('now triggering reboot');
  178. $.ajax({
  179. url: '/reboot_ota.json',
  180. dataType: 'text',
  181. method: 'POST',
  182. cache: false,
  183. contentType: 'application/json; charset=utf-8',
  184. data: JSON.stringify({ 'timestamp': Date.now()}),
  185. error: function (xhr, ajaxOptions, thrownError) {
  186. console.log(xhr.status);
  187. console.log(thrownError);
  188. if (thrownError != '') showMessage(thrownError, 'ERROR');
  189. },
  190. complete: function(response) {
  191. console.log('reboot call completed');
  192. }
  193. });
  194. }
  195. });
  196. });
  197. $("input#save-autoexec1").on("click", function() {
  198. var data = { 'timestamp': Date.now() };
  199. autoexec1 = $("#autoexec1").val();
  200. data['autoexec1'] = autoexec1;
  201. $.ajax({
  202. url: '/config.json',
  203. dataType: 'text',
  204. method: 'POST',
  205. cache: false,
  206. headers: { "X-Custom-autoexec1": autoexec1 },
  207. contentType: 'application/json; charset=utf-8',
  208. data: JSON.stringify(data),
  209. error: function (xhr, ajaxOptions, thrownError) {
  210. console.log(xhr.status);
  211. console.log(thrownError);
  212. if (thrownError != '') showMessage(thrownError, 'ERROR');
  213. }
  214. });
  215. console.log('sent config JSON with headers:', autoexec1);
  216. console.log('sent data:', JSON.stringify(data));
  217. });
  218. $("input#save-gpio").on("click", function() {
  219. var data = { 'timestamp': Date.now() };
  220. var headers = {};
  221. $("input.gpio").each(function() {
  222. var id = $(this)[0].id;
  223. var pin = $(this).val();
  224. if (pin != '') {
  225. headers["X-Custom-"+id] = pin;
  226. data[id] = pin;
  227. }
  228. });
  229. $.ajax({
  230. url: '/config.json',
  231. dataType: 'text',
  232. method: 'POST',
  233. cache: false,
  234. headers: headers,
  235. contentType: 'application/json; charset=utf-8',
  236. data: JSON.stringify(data),
  237. error: function (xhr, ajaxOptions, thrownError) {
  238. console.log(xhr.status);
  239. console.log(thrownError);
  240. if (thrownError != '') showMessage(thrownError, 'ERROR');
  241. }
  242. });
  243. console.log('sent config JSON with headers:', JSON.stringify(headers));
  244. console.log('sent config JSON with data:', JSON.stringify(data));
  245. });
  246. $("#save-nvs").on("click", function() {
  247. var headers = {};
  248. var data = { 'timestamp': Date.now() };
  249. $("input.nvs").each(function() {
  250. var key = $(this)[0].id;
  251. var val = $(this).val();
  252. if (key != '') {
  253. headers["X-Custom-"+key] = val;
  254. data[key] = {};
  255. data[key].value = val;
  256. data[key].type = 33;
  257. }
  258. });
  259. var key = $("#nvs-new-key").val();
  260. var val = $("#nvs-new-value").val();
  261. if (key != '') {
  262. headers["X-Custom-"+key] = val;
  263. data[key] = {};
  264. data[key].value = val;
  265. }
  266. $.ajax({
  267. url: '/config.json',
  268. dataType: 'text',
  269. method: 'POST',
  270. cache: false,
  271. headers: headers,
  272. contentType: 'application/json; charset=utf-8',
  273. data: JSON.stringify(data),
  274. error: function (xhr, ajaxOptions, thrownError) {
  275. console.log(xhr.status);
  276. console.log(thrownError);
  277. if (thrownError != '') showMessage(thrownError, 'ERROR');
  278. }
  279. });
  280. console.log('sent config JSON with headers:', JSON.stringify(headers));
  281. console.log('sent config JSON with data:', JSON.stringify(data));
  282. });
  283. $("#flash").on("click", function() {
  284. var data = { 'timestamp': Date.now() };
  285. if (blockFlashButton) return;
  286. blockFlashButton = true;
  287. var url = $("#fwurl").val();
  288. data['fwurl'] = url;
  289. $.ajax({
  290. url: '/config.json',
  291. dataType: 'text',
  292. method: 'POST',
  293. cache: false,
  294. headers: { "X-Custom-fwurl": url },
  295. contentType: 'application/json; charset=utf-8',
  296. data: JSON.stringify(data),
  297. error: function (xhr, ajaxOptions, thrownError) {
  298. console.log(xhr.status);
  299. console.log(thrownError);
  300. if (thrownError != '') showMessage(thrownError, 'ERROR');
  301. }
  302. });
  303. enableStatusTimer = true;
  304. });
  305. $("#generate-command").on("click", function() {
  306. var commandLine = commandHeader + ' -n "' + $("#player").val() + '"';
  307. if (output == 'bt') {
  308. commandLine += ' -o "BT -n \'' + $("#btsink").val() + '\'" -R -Z 192000';
  309. } else if (output == 'spdif') {
  310. commandLine += ' -o SPDIF -R -Z 192000';
  311. } else {
  312. commandLine += ' -o I2S';
  313. }
  314. if ($("#optional").val() != '') {
  315. commandLine += ' ' + $("#optional").val();
  316. }
  317. $("#autoexec1").val(commandLine);
  318. });
  319. $('[name=audio]').on("click", function(){
  320. if (this.id == 'bt') {
  321. $("#btsinkdiv").show(200);
  322. output = 'bt';
  323. } else if (this.id == 'spdif') {
  324. $("#btsinkdiv").hide(200);
  325. output = 'spdif';
  326. } else {
  327. $("#btsinkdiv").hide(200);
  328. output = 'i2s';
  329. }
  330. });
  331. $('#fwcheck').on("click", function(){
  332. $("#releaseTable").html("");
  333. $.getJSON(releaseURL, function(data) {
  334. var i=0;
  335. data.forEach(function(release) {
  336. var url = '';
  337. release.assets.forEach(function(asset) {
  338. if (asset.name.match(/\.bin$/)) {
  339. url = asset.browser_download_url;
  340. }
  341. });
  342. var [ver, idf, cfg, branch] = release.name.split('#');
  343. var body = release.body;
  344. body = body.replace(/\'/ig, "\"");
  345. body = body.replace(/[\s\S]+(### Revision Log[\s\S]+)### ESP-IDF Version Used[\s\S]+/, "$1");
  346. body = body.replace(/- \(.+?\) /g, "- ");
  347. var [date, time] = release.created_at.split('T');
  348. var trclass = (i++ > 6)?' hide':'';
  349. $("#releaseTable").append(
  350. "<tr class='release"+trclass+"'>"+
  351. "<td data-toggle='tooltip' title='"+body+"'>"+ver+"</td>"+
  352. "<td>"+date+"</td>"+
  353. "<td>"+cfg+"</td>"+
  354. "<td>"+idf+"</td>"+
  355. "<td>"+branch+"</td>"+
  356. "<td><input id='generate-command' type='button' class='btn btn-success' value='Select' data-url='"+url+"' onclick='setURL(this);' /></td>"+
  357. "</tr>"
  358. );
  359. });
  360. if (i > 7) {
  361. $("#releaseTable").append(
  362. "<tr id='showall'>"+
  363. "<td colspan='6'>"+
  364. "<input type='button' id='showallbutton' class='btn btn-info' value='Show older releases' />"+
  365. "</td>"+
  366. "</tr>"
  367. );
  368. $('#showallbutton').on("click", function(){
  369. $("tr.hide").removeClass("hide");
  370. $("tr#showall").addClass("hide");
  371. });
  372. }
  373. $("#searchfw").css("display", "inline");
  374. })
  375. .fail(function() {
  376. alert("failed to fetch release history!");
  377. });
  378. });
  379. $('input#searchinput').on("input", function(){
  380. var s = $('input#searchinput').val();
  381. var re = new RegExp(s, "gi");
  382. if (s.length == 0) {
  383. $("tr.release").removeClass("hide");
  384. } else if (s.length < 3) {
  385. $("tr.release").addClass("hide");
  386. } else {
  387. $("tr.release").addClass("hide");
  388. $("tr.release").each(function(tr){
  389. $(this).find('td').each (function() {
  390. if ($(this).html().match(re)) {
  391. $(this).parent().removeClass('hide');
  392. }
  393. });
  394. });
  395. }
  396. });
  397. $('#boot-button').on("click", function(){
  398. enableStatusTimer = true;
  399. });
  400. $('#reboot-button').on("click", function(){
  401. enableStatusTimer = true;
  402. });
  403. $('#updateAP').on("click", function(){
  404. refreshAP(true);
  405. console.log("refresh AP");
  406. });
  407. //first time the page loads: attempt to get the connection status and start the wifi scan
  408. refreshAP(false);
  409. getConfig();
  410. //start timers
  411. startCheckStatusInterval();
  412. //startRefreshAPInterval();
  413. $('[data-toggle="tooltip"]').tooltip({
  414. html: true,
  415. placement : 'right',
  416. });
  417. });
  418. function setURL(button) {
  419. var url = button.dataset.url;
  420. $("#fwurl").val(url);
  421. $('[data-url^="http"]').addClass("btn-success").removeClass("btn-danger");
  422. $('[data-url="'+url+'"]').addClass("btn-danger").removeClass("btn-success");
  423. }
  424. function performConnect(conntype){
  425. //stop the status refresh. This prevents a race condition where a status
  426. //request would be refreshed with wrong ip info from a previous connection
  427. //and the request would automatically shows as succesful.
  428. stopCheckStatusInterval();
  429. //stop refreshing wifi list
  430. stopRefreshAPInterval();
  431. var pwd;
  432. var dhcpname;
  433. if (conntype == 'manual') {
  434. //Grab the manual SSID and PWD
  435. selectedSSID=$('#manual_ssid').val();
  436. pwd = $("#manual_pwd").val();
  437. dhcpname= $("#dhcp-name2").val();;
  438. }else{
  439. pwd = $("#pwd").val();
  440. dhcpname= $("#dhcp-name1").val();;
  441. }
  442. //reset connection
  443. $( "#loading" ).show();
  444. $( "#connect-success" ).hide();
  445. $( "#connect-fail" ).hide();
  446. $( "#ok-connect" ).prop("disabled",true);
  447. $( "#ssid-wait" ).text(selectedSSID);
  448. $( "#connect" ).slideUp( "fast", function() {});
  449. $( "#connect_manual" ).slideUp( "fast", function() {});
  450. $( "#connect-wait" ).slideDown( "fast", function() {});
  451. $.ajax({
  452. url: '/connect.json',
  453. dataType: 'text',
  454. method: 'POST',
  455. cache: false,
  456. headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd, 'X-Custom-host_name': dhcpname },
  457. contentType: 'application/json; charset=utf-8',
  458. data: { 'timestamp': Date.now()},
  459. error: function (xhr, ajaxOptions, thrownError) {
  460. console.log(xhr.status);
  461. console.log(thrownError);
  462. if (thrownError != '') showMessage(thrownError, 'ERROR');
  463. }
  464. });
  465. //now we can re-set the intervals regardless of result
  466. startCheckStatusInterval();
  467. startRefreshAPInterval();
  468. }
  469. function rssiToIcon(rssi){
  470. if(rssi >= -60){
  471. return 'w0';
  472. }
  473. else if(rssi >= -67){
  474. return 'w1';
  475. }
  476. else if(rssi >= -75){
  477. return 'w2';
  478. }
  479. else{
  480. return 'w3';
  481. }
  482. }
  483. function refreshAP(force){
  484. if (!enableAPTimer && !force) return;
  485. $.getJSON( "/ap.json", function( data ) {
  486. if(data.length > 0){
  487. //sort by signal strength
  488. data.sort(function (a, b) {
  489. var x = a["rssi"]; var y = b["rssi"];
  490. return ((x < y) ? 1 : ((x > y) ? -1 : 0));
  491. });
  492. apList = data;
  493. refreshAPHTML(apList);
  494. }
  495. });
  496. }
  497. function refreshAPHTML(data){
  498. var h = "";
  499. data.forEach(function(e, idx, array) {
  500. 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);
  501. h += "\n";
  502. });
  503. $( "#wifi-list" ).html(h)
  504. }
  505. function checkStatus(){
  506. RepeatCheckStatusInterval();
  507. if (!enableStatusTimer) return;
  508. if (blockAjax) return;
  509. blockAjax = true;
  510. $.getJSON( "/status.json", function( data ) {
  511. if (data.hasOwnProperty('ssid') && data['ssid'] != ""){
  512. if (data["ssid"] === selectedSSID){
  513. //that's a connection attempt
  514. if (data["urc"] === 0){
  515. //got connection
  516. $("#connected-to span").text(data["ssid"]);
  517. $("#connect-details h1").text(data["ssid"]);
  518. $("#ip").text(data["ip"]);
  519. $("#netmask").text(data["netmask"]);
  520. $("#gw").text(data["gw"]);
  521. $("#wifi-status").slideDown( "fast", function() {});
  522. $("span#foot-wifi").html(", SSID: <strong>"+data["ssid"]+"</strong>, IP: <strong>"+data["ip"]+"</strong>");
  523. //unlock the wait screen if needed
  524. $( "#ok-connect" ).prop("disabled",false);
  525. //update wait screen
  526. $( "#loading" ).hide();
  527. $( "#connect-success" ).text("Your IP address now is: " + data["ip"] );
  528. $( "#connect-success" ).show();
  529. $( "#connect-fail" ).hide();
  530. enableAPTimer = false;
  531. if (!recovery) enableStatusTimer = false;
  532. }
  533. else if (data["urc"] === 1){
  534. //failed attempt
  535. $("#connected-to span").text('');
  536. $("#connect-details h1").text('');
  537. $("#ip").text('0.0.0.0');
  538. $("#netmask").text('0.0.0.0');
  539. $("#gw").text('0.0.0.0');
  540. $("span#foot-wifi").html("");
  541. //don't show any connection
  542. $("#wifi-status").slideUp( "fast", function() {});
  543. //unlock the wait screen
  544. $( "#ok-connect" ).prop("disabled",false);
  545. //update wait screen
  546. $( "#loading" ).hide();
  547. $( "#connect-fail" ).show();
  548. $( "#connect-success" ).hide();
  549. enableAPTimer = true;
  550. enableStatusTimer = true;
  551. }
  552. }
  553. else if (data.hasOwnProperty('urc') && data['urc'] === 0){
  554. //ESP32 is already connected to a wifi without having the user do anything
  555. if( !($("#wifi-status").is(":visible")) ){
  556. $("#connected-to span").text(data["ssid"]);
  557. $("#connect-details h1").text(data["ssid"]);
  558. $("#ip").text(data["ip"]);
  559. $("#netmask").text(data["netmask"]);
  560. $("#gw").text(data["gw"]);
  561. $("#wifi-status").slideDown( "fast", function() {});
  562. $("span#foot-wifi").html(", SSID: <strong>"+data["ssid"]+"</strong>, IP: <strong>"+data["ip"]+"</strong>");
  563. }
  564. enableAPTimer = false;
  565. if (!recovery) enableStatusTimer = false;
  566. }
  567. }
  568. else if (data.hasOwnProperty('urc') && data['urc'] === 2){
  569. //that's a manual disconnect
  570. if($("#wifi-status").is(":visible")){
  571. $("#wifi-status").slideUp( "fast", function() {});
  572. $("span#foot-wifi").html("");
  573. }
  574. enableAPTimer = true;
  575. enableStatusTimer = true;
  576. }
  577. if (data.hasOwnProperty('recovery')) {
  578. if(LastRecoveryState != data["recovery"]){
  579. LastRecoveryState = data["recovery"];
  580. $("input#show-nvs")[0].checked=LastRecoveryState==1?true:false;
  581. }
  582. if($("input#show-nvs")[0].checked){
  583. $('a[href^="#tab-nvs"]').show();
  584. } else{
  585. $('a[href^="#tab-nvs"]').hide();
  586. }
  587. if (data["recovery"] === 1) {
  588. recovery = true;
  589. $("#otadiv").show();
  590. $('a[href^="#tab-audio"]').hide();
  591. $('a[href^="#tab-gpio"]').show();
  592. $("footer.footer").removeClass('sl');
  593. $("footer.footer").addClass('recovery');
  594. $("#boot-button").html('Reboot');
  595. $("#boot-form").attr('action', '/reboot_ota.json');
  596. enableStatusTimer = true;
  597. } else {
  598. recovery = false;
  599. $("#otadiv").hide();
  600. $('a[href^="#tab-audio"]').show();
  601. $('a[href^="#tab-gpio"]').hide();
  602. $("footer.footer").removeClass('recovery');
  603. $("footer.footer").addClass('sl');
  604. $("#boot-button").html('Recovery');
  605. $("#boot-form").attr('action', '/recovery.json');
  606. enableStatusTimer = false;
  607. }
  608. }
  609. if (data.hasOwnProperty('project_name') && data['project_name'] != ''){
  610. pname = data['project_name'];
  611. }
  612. if (data.hasOwnProperty('version') && data['version'] != ''){
  613. ver = data['version'];
  614. $("span#foot-fw").html("fw: <strong>"+ver+"</strong>, mode: <strong>"+pname+"</strong>");
  615. }
  616. if (data.hasOwnProperty('ota_pct') && data['ota_pct'] != 0){
  617. otapct = data['ota_pct'];
  618. $('.progress-bar').css('width', otapct+'%').attr('aria-valuenow', otapct);
  619. $('.progress-bar').html(otapct+'%');
  620. }
  621. if (data.hasOwnProperty('ota_dsc') && data['ota_dsc'] != ''){
  622. otadsc = data['ota_dsc'];
  623. $("span#flash-status").html(otadsc);
  624. if (otadsc.match(/Error:/) || otapct > 95) {
  625. blockFlashButton = false;
  626. enableStatusTimer = true;
  627. }
  628. } else {
  629. $("span#flash-status").html('');
  630. }
  631. if (data.hasOwnProperty('message') && data['message'] != ''){
  632. var msg = data['message'].text;
  633. var severity = data['message'].severity;
  634. if (msg != lastMsg) {
  635. showMessage(msg, severity);
  636. lastMsg = msg;
  637. }
  638. }
  639. if (data.hasOwnProperty('Voltage')) {
  640. var voltage = data['Voltage'];
  641. var layer;
  642. if (voltage > 0) {
  643. if (inRange(voltage, 5.8, 6.2) || inRange(voltage, 8.8, 9.2)) {
  644. layer = bat0;
  645. } else if (inRange(voltage, 6.2, 6.8) || inRange(voltage, 9.2, 10.0)) {
  646. layer = bat1;
  647. } else if (inRange(voltage, 6.8, 7.1) || inRange(voltage, 10.0, 10.5)) {
  648. layer = bat2;
  649. } else if (inRange(voltage, 7.1, 7.5) || inRange(voltage, 10.5, 11.0)) {
  650. layer = bat3;
  651. } else {
  652. layer = bat4;
  653. }
  654. layer.setAttribute("display","inline");
  655. }
  656. }
  657. if (data.hasOwnProperty('Jack')) {
  658. var jack = data['Jack'];
  659. if (jack == '1') {
  660. o_jack.setAttribute("display","inline");
  661. }
  662. }
  663. blockAjax = false;
  664. })
  665. .fail(function(xhr, ajaxOptions, thrownError) {
  666. console.log(xhr.status);
  667. console.log(thrownError);
  668. if (thrownError != '') showMessage(thrownError, 'ERROR');
  669. blockAjax = false;
  670. });
  671. }
  672. function getConfig() {
  673. $.getJSON("/config.json", function(data) {
  674. Object.keys(data).sort().forEach(function(key, i) {
  675. if (data.hasOwnProperty(key)) {
  676. if (key == 'autoexec') {
  677. if (data["autoexec"].value === "1") {
  678. $("#autoexec-cb")[0].checked=true;
  679. } else {
  680. $("#autoexec-cb")[0].checked=false;
  681. }
  682. } else if (key == 'autoexec1') {
  683. $("textarea#autoexec1").val(data[key].value);
  684. var re = / -o "?(\S+)\b/g;
  685. var m = re.exec(data[key].value);
  686. if (m[1] =='I2S') {
  687. o_i2s.setAttribute("display","inline");
  688. } else if (m[1] =='SPDIF') {
  689. o_spdif.setAttribute("display","inline");
  690. } else if (m[1] =='BT') {
  691. o_bt.setAttribute("display","inline");
  692. }
  693. } else if (key == 'host_name') {
  694. $("input#dhcp-name1").val(data[key].value);
  695. $("input#dhcp-name2").val(data[key].value);
  696. }
  697. $("tbody#nvsTable").append(
  698. "<tr>"+
  699. "<td>"+key+"</td>"+
  700. "<td class='value'>"+
  701. "<input type='text' class='form-control nvs' id='"+key+"'>"+
  702. "</td>"+
  703. "</tr>"
  704. );
  705. $("input#"+key).val(data[key].value);
  706. }
  707. });
  708. $("tbody#nvsTable").append(
  709. "<tr>"+
  710. "<td>"+
  711. "<input type='text' class='form-control' id='nvs-new-key' placeholder='new key'>"+
  712. "</td>"+
  713. "<td>"+
  714. "<input type='text' class='form-control' id='nvs-new-value' placeholder='new value'>"+
  715. "</td>"+
  716. "</tr>"
  717. );
  718. })
  719. .fail(function(xhr, ajaxOptions, thrownError) {
  720. console.log(xhr.status);
  721. console.log(thrownError);
  722. if (thrownError != '') showMessage(thrownError, 'ERROR');
  723. blockAjax = false;
  724. });
  725. }
  726. function showMessage(message, severity) {
  727. if (severity == 'INFO') {
  728. $('#message').css('background', '#6af');
  729. } else if (severity == 'WARNING') {
  730. $('#message').css('background', '#ff0');
  731. } else {
  732. $('#message').css('background', '#f00');
  733. }
  734. $('#message').html(message);
  735. $("#content").fadeTo("slow", 0.3, function() {
  736. $("#message").show(500).delay(5000).hide(500, function() {
  737. $("#content").fadeTo("slow", 1.0);
  738. });
  739. });
  740. }
  741. function inRange(x, min, max) {
  742. return ((x-min)*(x-max) <= 0);
  743. }