import he from 'he';
import { Promise } from 'es6-promise';
if (!String.prototype.format) {
Object.assign(String.prototype, {
format() {
const args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] !== 'undefined' ? args[number] : match;
});
},
});
}
if (!String.prototype.encodeHTML) {
Object.assign(String.prototype, {
encodeHTML() {
return he.encode(this).replace(/\n/g, '
')
},
});
}
Object.assign(Date.prototype, {
toLocalShort() {
const opt = { dateStyle: 'short', timeStyle: 'short' };
return this.toLocaleString(undefined, opt);
},
});
const nvsTypes = {
NVS_TYPE_U8: 0x01,
/*! < Type uint8_t */
NVS_TYPE_I8: 0x11,
/*! < Type int8_t */
NVS_TYPE_U16: 0x02,
/*! < Type uint16_t */
NVS_TYPE_I16: 0x12,
/*! < Type int16_t */
NVS_TYPE_U32: 0x04,
/*! < Type uint32_t */
NVS_TYPE_I32: 0x14,
/*! < Type int32_t */
NVS_TYPE_U64: 0x08,
/*! < Type uint64_t */
NVS_TYPE_I64: 0x18,
/*! < Type int64_t */
NVS_TYPE_STR: 0x21,
/*! < Type string */
NVS_TYPE_BLOB: 0x42,
/*! < Type blob */
NVS_TYPE_ANY: 0xff /*! < Must be last */,
};
const btIcons = {
bt_playing: 'play-circle-fill',
bt_disconnected: 'bluetooth-fill',
bt_neutral: '',
bt_connected: 'bluetooth-connect-fill',
bt_disabled: '',
play_arrow: 'play-circle-fill',
pause: 'pause-circle-fill',
stop: 'stop-circle-fill',
'': '',
};
const btStateIcons = [
{ desc: 'Idle', sub: ['bt_neutral'] },
{ desc: 'Discovering', sub: ['bt_disconnected'] },
{ desc: 'Discovered', sub: ['bt_disconnected'] },
{ desc: 'Unconnected', sub: ['bt_disconnected'] },
{ desc: 'Connecting', sub: ['bt_disconnected'] },
{
desc: 'Connected',
sub: ['bt_connected', 'play_arrow', 'bt_playing', 'pause', 'stop'],
},
{ desc: 'Disconnecting', sub: ['bt_disconnected'] },
];
const pillcolors = {
MESSAGING_INFO: 'badge-success',
MESSAGING_WARNING: 'badge-warning',
MESSAGING_ERROR: 'badge-danger',
};
const connectReturnCode = {
UPDATE_CONNECTION_OK : 0,
UPDATE_FAILED_ATTEMPT : 1,
UPDATE_USER_DISCONNECT : 2,
UPDATE_LOST_CONNECTION : 3,
UPDATE_FAILED_ATTEMPT_AND_RESTORE : 4
}
const taskStates = {
0: 'eRunning',
/*! < A task is querying the state of itself, so must be running. */
1: 'eReady',
/*! < The task being queried is in a read or pending ready list. */
2: 'eBlocked',
/*! < The task being queried is in the Blocked state. */
3: 'eSuspended',
/*! < The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
4: 'eDeleted',
};
window.hideSurrounding = function(obj){
$(obj).parent().parent().hide()
}
window.handleReboot = function(ota){
if(ota){
$('#reboot_ota_nav').removeClass('active'); delayReboot(500,'', true);
}
else {
$('#reboot_nav').removeClass('active'); delayReboot(500,'', false);
}
}
function handlebtstate(data) {
let icon = '';
let tt = '';
if (data.bt_status !== undefined && data.bt_sub_status !== undefined) {
const iconsvg = btStateIcons[data.bt_status].sub[data.bt_sub_status];
if (iconsvg) {
icon = `#${btIcons[iconsvg]}`;
tt = btStateIcons[data.bt_status].desc;
} else {
icon = `#${btIcons.bt_connected}`;
tt = 'Output status';
}
}
$('#o_type').title = tt;
$('#o_bt').attr('xlink:href',icon);
}
function handleTemplateTypeRadio(outtype) {
if (outtype === 'bt') {
$('#bt').prop('checked', true);
$('#o_bt').attr('display', 'inline');
$('#o_spdif').attr('display', 'none');
$('#o_i2s').attr('display', 'none');
output = 'bt';
} else if (outtype === 'spdif') {
$('#spdif').prop('checked', true);
$('#o_bt').attr('display', 'none');
$('#o_spdif').attr('display', 'inline');
$('#o_i2s').attr('display', 'none');
output = 'spdif';
} else {
$('#i2s').prop('checked', true);
$('#o_bt').attr('display', 'none');
$('#o_spdif').attr('display', 'none');
$('#o_i2s').attr('display', 'inline');
output = 'i2s';
}
}
function handleExceptionResponse(xhr, _ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
enableStatusTimer = true;
if (thrownError !== '') {
showLocalMessage(thrownError, 'MESSAGING_ERROR');
}
}
function HideCmdMessage(cmdname) {
$('#toast_' + cmdname).css('display', 'none');
$('#toast_' + cmdname)
.removeClass('table-success')
.removeClass('table-warning')
.removeClass('table-danger')
.addClass('table-success');
$('#msg_' + cmdname).html('');
}
function showCmdMessage(cmdname, msgtype, msgtext, append = false) {
let color = 'table-success';
if (msgtype === 'MESSAGING_WARNING') {
color = 'table-warning';
} else if (msgtype === 'MESSAGING_ERROR') {
color = 'table-danger';
}
$('#toast_' + cmdname).css('display', 'block');
$('#toast_' + cmdname)
.removeClass('table-success')
.removeClass('table-warning')
.removeClass('table-danger')
.addClass(color);
let escapedtext = msgtext
.substring(0, msgtext.length - 1)
.encodeHTML()
.replace(/\n/g, '
');
escapedtext =
($('#msg_' + cmdname).html().length > 0 && append
? $('#msg_' + cmdname).html() + '
'
: '') + escapedtext;
$('#msg_' + cmdname).html(escapedtext);
}
const releaseURL =
'https://api.github.com/repos/sle118/squeezelite-esp32/releases';
let recovery = false;
var enableStatusTimer = true;
const commandHeader = 'squeezelite -b 500:2000 -d all=info -C 30 -W';
let otapct, otadsc;
let blockAjax = false;
let blockFlashButton = false;
let apList = null;
//let selectedSSID = '';
//let checkStatusInterval = null;
let messagecount = 0;
let messageseverity = 'MESSAGING_INFO';
let StatusIntervalActive = false;
let LastRecoveryState = null;
let SystemConfig={};
let LastCommandsState = null;
var output = '';
let hostName = '';
let versionName='SqueezeESP32';
let appTitle=versionName;
let ConnectedToSSID={};
let ConnectingToSSID={};
const ConnectingToActions = {
'CONN' : 0,'MAN' : 1,'STS' : 2,
}
Promise.prototype.delay = function(duration) {
return this.then(
function(value) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(value);
}, duration);
});
},
function(reason) {
return new Promise(function(_resolve, reject) {
setTimeout(function() {
reject(reason);
}, duration);
});
}
);
};
// function stopCheckStatusInterval() {
// if (checkStatusInterval != null) {
// clearTimeout(checkStatusInterval);
// checkStatusInterval = null;
// }
// StatusIntervalActive = false;
// }
function startCheckStatusInterval() {
StatusIntervalActive = true;
setTimeout(checkStatus, 3000);
}
function RepeatCheckStatusInterval() {
if (StatusIntervalActive) {
startCheckStatusInterval();
}
}
function getConfigJson(slimMode) {
const config = {};
$('input.nvs').each(function(_index, entry) {
if (!slimMode) {
const nvsType = parseInt(entry.attributes.nvs_type.value, 10);
if (entry.id !== '') {
config[entry.id] = {};
if (
nvsType === nvsTypes.NVS_TYPE_U8 ||
nvsType === nvsTypes.NVS_TYPE_I8 ||
nvsType === nvsTypes.NVS_TYPE_U16 ||
nvsType === nvsTypes.NVS_TYPE_I16 ||
nvsType === nvsTypes.NVS_TYPE_U32 ||
nvsType === nvsTypes.NVS_TYPE_I32 ||
nvsType === nvsTypes.NVS_TYPE_U64 ||
nvsType === nvsTypes.NVS_TYPE_I64
) {
config[entry.id].value = parseInt(entry.value);
} else {
config[entry.id].value = entry.value;
}
config[entry.id].type = nvsType;
}
} else {
config[entry.id] = entry.value;
}
});
const key = $('#nvs-new-key').val();
const val = $('#nvs-new-value').val();
if (key !== '') {
if (!slimMode) {
config[key] = {};
config[key].value = val;
config[key].type = 33;
} else {
config[key] = val;
}
}
return config;
}
// eslint-disable-next-line no-unused-vars
function onFileLoad(elementId, event) {
let data = {};
try {
data = JSON.parse(elementId.srcElement.result);
} catch (e) {
alert('Parsing failed!\r\n ' + e);
}
$('input.nvs').each(function(_index, entry) {
if (data[entry.id]) {
if (data[entry.id] !== entry.value) {
console.log(
'Changed ' + entry.id + ' ' + entry.value + '==>' + data[entry.id]
);
$(this).val(data[entry.id]);
}
}
});
}
// eslint-disable-next-line no-unused-vars
function onChooseFile(event, onLoadFileHandler) {
if (typeof window.FileReader !== 'function') {
throw "The file API isn't supported on this browser.";
}
const input = event.target;
if (!input) {
throw 'The browser does not properly implement the event object';
}
if (!input.files) {
throw 'This browser does not support the `files` property of the file input.';
}
if (!input.files[0]) {
return undefined;
}
const file = input.files[0];
let fr = new FileReader();
fr.onload = onLoadFileHandler;
fr.readAsText(file);
input.value = '';
}
function delayReboot(duration, cmdname, ota = false) {
const url = ota ? '/reboot_ota.json' : '/reboot.json';
$('tbody#tasks').empty();
enableStatusTimer = false;
$('#tasks_sect').css('visibility', 'collapse');
Promise.resolve({ cmdname: cmdname, url: url })
.delay(duration)
.then(function(data) {
if (data.cmdname.length > 0) {
showCmdMessage(
data.cmdname,
'MESSAGING_WARNING',
'System is rebooting.\n',
true
);
} else {
showLocalMessage('System is rebooting.\n', 'MESSAGING_WARNING');
}
console.log('now triggering reboot');
$.ajax({
url: data.url,
dataType: 'text',
method: 'POST',
cache: false,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
timestamp: Date.now(),
}),
error: handleExceptionResponse,
complete: function() {
console.log('reboot call completed');
enableStatusTimer = true;
Promise.resolve(data)
.delay(6000)
.then(function(rdata) {
if (rdata.cmdname.length > 0) {
HideCmdMessage(rdata.cmdname);
}
getCommands();
getConfig();
});
},
});
});
}
// eslint-disable-next-line no-unused-vars
window.saveAutoexec1 = function(apply) {
showCmdMessage('cfg-audio-tmpl', 'MESSAGING_INFO', 'Saving.\n', false);
let commandLine = commandHeader + ' -n "' + $('#player').val() + '"';
if (output === 'bt') {
commandLine += ' -o "BT" -R -Z 192000';
showCmdMessage(
'cfg-audio-tmpl',
'MESSAGING_INFO',
'Remember to configure the Bluetooth audio device name.\n',
true
);
} else if (output === 'spdif') {
commandLine += ' -o SPDIF -Z 192000';
} else {
commandLine += ' -o I2S';
}
if ($('#optional').val() !== '') {
commandLine += ' ' + $('#optional').val();
}
const data = {
timestamp: Date.now(),
};
data.config = {
autoexec1: { value: commandLine, type: 33 },
autoexec: {
value: $('#disable-squeezelite').prop('checked') ? '0' : '1',
type: 33,
},
};
$.ajax({
url: '/config.json',
dataType: 'text',
method: 'POST',
cache: false,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
error: handleExceptionResponse,
complete: function(response) {
if (
response.responseText.result &&
JSON.parse(response.responseText).result === 'OK'
) {
showCmdMessage('cfg-audio-tmpl', 'MESSAGING_INFO', 'Done.\n', true);
if (apply) {
delayReboot(1500, 'cfg-audio-tmpl');
}
} else if (response.responseText.result) {
showCmdMessage(
'cfg-audio-tmpl',
'MESSAGING_WARNING',
JSON.parse(response.responseText).Result + '\n',
true
);
} else {
showCmdMessage(
'cfg-audio-tmpl',
'MESSAGING_ERROR',
response.statusText + '\n'
);
}
console.log(response.responseText);
},
});
console.log('sent data:', JSON.stringify(data));
}
window.handleDisconnect = function(){
$.ajax({
url: '/connect.json',
dataType: 'text',
method: 'DELETE',
cache: false,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
timestamp: Date.now(),
}),
});
}
window.handleConnect = function(){
ConnectingToSSID.ssid = $('#manual_ssid').val();
ConnectingToSSID.pwd = $('#manual_pwd').val();
ConnectingToSSID.dhcpname = $('#dhcp-name2').val();
$("*[class*='connecting']").hide();
$('#ssid-wait').text(ConnectingToSSID.ssid);
$('.connecting').show();
$.ajax({
url: '/connect.json',
dataType: 'text',
method: 'POST',
cache: false,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
timestamp: Date.now(),
ssid: ConnectingToSSID.ssid,
pwd: ConnectingToSSID.pwd
}),
error: handleExceptionResponse,
});
// now we can re-set the intervals regardless of result
startCheckStatusInterval();
}
$(document).ready(function() {
setTimeout(refreshAP,1500);
$('#WifiConnectDialog').on('shown.bs.modal', function () {
$("*[class*='connecting']").hide();
if(ConnectingToSSID.Action!==ConnectingToActions.STS){
$('.connecting-init').show();
$('#manual_ssid').trigger('focus');
}
else {
handleWifiDialog();
}
})
$('#WifiConnectDialog').on('hidden.bs.modal', function () {
$('#WifiConnectDialog input').val('');
})
$('input#show-commands')[0].checked = LastCommandsState === 1;
$('a[href^="#tab-commands"]').hide();
$('#load-nvs').on('click', function() {
$('#nvsfilename').trigger('click');
});
$('#clear-syslog').on('click', function() {
messagecount = 0;
messageseverity = 'MESSAGING_INFO';
$('#msgcnt').text('');
$('#syslogTable').html('');
});
$('#wifiTable').on('click','tr', function() {
ConnectingToSSID.Action=ConnectingToActions.CONN;
if($(this).children('td:eq(1)').text() == ConnectedToSSID.ssid){
ConnectingToSSID.Action=ConnectingToActions.STS;
return;
}
if(!$(this).is(':last-child')){
ConnectingToSSID.ssid=$(this).children('td:eq(1)').text();
$('#manual_ssid').val(ConnectingToSSID.ssid);
}
else {
ConnectingToSSID.Action=ConnectingToActions.MAN;
ConnectingToSSID.ssid='';
$('#manual_ssid').val(ConnectingToSSID.ssid);
}
});
// $('#cancel').on('click', function() {
// selectedSSID = '';
// $('#connect').slideUp('fast', function() {});
// $('#connect_manual').slideUp('fast', function() {});
// $('#wifi').slideDown('fast', function() {});
// });
// $('#manual_cancel').on('click', function() {
// selectedSSID = '';
// $('#connect').slideUp('fast', function() {});
// $('#connect_manual').slideUp('fast', function() {});
// $('#wifi').slideDown('fast', function() {});
// });
// $('#ok-details').on('click', function() {
// $('#connect-details').slideUp('fast', function() {});
// $('#wifi').slideDown('fast', function() {});
// });
$('#ok-credits').on('click', function() {
$('#credits').slideUp('fast', function() {});
$('#app').slideDown('fast', function() {});
});
$('#acredits').on('click', function(event) {
event.preventDefault();
$('#app').slideUp('fast', function() {});
$('#credits').slideDown('fast', function() {});
});
// $('#disconnect').on('click', function() {
// $('#connect-details-wrap').addClass('blur');
// $('#diag-disconnect').slideDown('fast', function() {});
// });
// $('#no-disconnect').on('click', function() {
// $('#diag-disconnect').slideUp('fast', function() {});
// $('#connect-details-wrap').removeClass('blur');
// });
// $('#yes-disconnect').on('click', function() {
// stopCheckStatusInterval();
// selectedSSID = '';
// $('#diag-disconnect').slideUp('fast', function() {});
// $('#connect-details-wrap').removeClass('blur');
// $.ajax({
// url: '/connect.json',
// dataType: 'text',
// method: 'DELETE',
// cache: false,
// contentType: 'application/json; charset=utf-8',
// data: JSON.stringify({
// timestamp: Date.now(),
// }),
// });
// startCheckStatusInterval();
// $('#connect-details').slideUp('fast', function() {});
// $('#wifi').slideDown('fast', function() {});
// });
$('input#show-commands').on('click', function() {
this.checked = this.checked ? 1 : 0;
if (this.checked) {
$('a[href^="#tab-commands"]').show();
LastCommandsState = 1;
} else {
LastCommandsState = 0;
$('a[href^="#tab-commands"]').hide();
}
});
$('input#show-nvs').on('click', function() {
this.checked = this.checked ? 1 : 0;
if (this.checked) {
$('*[href*="-nvs"]').show();
} else {
$('*[href*="-nvs"]').hide();
}
});
$('#save-as-nvs').on('click', function() {
const config = getConfigJson(true);
const a = document.createElement('a');
a.href = URL.createObjectURL(
new Blob([JSON.stringify(config, null, 2)], {
type: 'text/plain',
})
);
a.setAttribute(
'download',
'nvs_config_' + hostName + '_' + Date.now() + 'json'
);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
$('#save-nvs').on('click', function() {
const headers = {};
const data = {
timestamp: Date.now(),
};
const config = getConfigJson(false);
data.config = config;
$.ajax({
url: '/config.json',
dataType: 'text',
method: 'POST',
cache: false,
headers: headers,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
error: handleExceptionResponse,
});
console.log('sent config JSON with headers:', JSON.stringify(headers));
console.log('sent config JSON with data:', JSON.stringify(data));
});
$('#fwUpload').on('click', function() {
const uploadPath = '/flash.json';
if (!recovery) {
$('#flash-status').text('Rebooting to recovery. Please try again');
window.handleReboot(false);
}
const fileInput = document.getElementById('flashfilename').files;
if (fileInput.length === 0) {
alert('No file selected!');
} else {
const file = fileInput[0];
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState === 4) {
if (xhttp.status === 200) {
showLocalMessage(xhttp.responseText, 'MESSAGING_INFO');
} else if (xhttp.status === 0) {
showLocalMessage(
'Upload connection was closed abruptly!',
'MESSAGING_ERROR'
);
} else {
showLocalMessage(
xhttp.status + ' Error!\n' + xhttp.responseText,
'MESSAGING_ERROR'
);
}
}
};
xhttp.open('POST', uploadPath, true);
xhttp.send(file);
}
enableStatusTimer = true;
});
$('#flash').on('click', function() {
const data = {
timestamp: Date.now(),
};
if (blockFlashButton) {
return;
}
blockFlashButton = true;
const url = $('#fwurl').val();
data.config = {
fwurl: {
value: url,
type: 33,
},
};
$.ajax({
url: '/config.json',
dataType: 'text',
method: 'POST',
cache: false,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
error: handleExceptionResponse,
});
enableStatusTimer = true;
});
$('[name=output-tmpl]').on('click', function() {
handleTemplateTypeRadio(this.id);
});
$('#fwcheck').on('click', function() {
$('#releaseTable').html('');
$('#fwbranch').empty();
$.getJSON(releaseURL, function(data) {
let i = 0;
const branches = [];
data.forEach(function(release) {
const namecomponents = release.name.split('#');
const branch = namecomponents[3];
if (!branches.includes(branch)) {
branches.push(branch);
}
});
let fwb;
branches.forEach(function(branch) {
fwb += '';
});
$('#fwbranch').append(fwb);
data.forEach(function(release) {
let url = '';
release.assets.forEach(function(asset) {
if (asset.name.match(/\.bin$/)) {
url = asset.browser_download_url;
}
});
const namecomponents = release.name.split('#');
const ver = namecomponents[0];
const idf = namecomponents[1];
const cfg = namecomponents[2];
const branch = namecomponents[3];
let body = release.body;
body = body.replace(/'/gi, '"');
body = body.replace(
/[\s\S]+(### Revision Log[\s\S]+)### ESP-IDF Version Used[\s\S]+/,
'$1'
);
body = body.replace(/- \(.+?\) /g, '- ');
const trclass = i++ > 6 ? ' hide' : '';
$('#releaseTable').append(
"