Improved audio ui, bug fixes

This commit is contained in:
Sebastien L
2023-04-07 10:53:22 -04:00
parent 1e0fce53c7
commit 7ac628a29d
34 changed files with 1176 additions and 169 deletions

View File

@@ -266,6 +266,11 @@ cJSON* network_status_get_basic_info(cJSON** old) {
*old = network_status_update_float(old, "avg_conn_time", nm->num_disconnect > 0 ? (nm->total_connected_time / nm->num_disconnect) : 0);
*old = network_update_cjson_number(old, "bt_status", bt_app_source_get_a2d_state());
*old = network_update_cjson_number(old, "bt_sub_status", bt_app_source_get_media_state());
#if DEPTH == 16
*old = network_update_cjson_number(old, "depth", 16);
#elif DEPTH == 32
*old = network_update_cjson_number(old, "depth", 32);
#endif
#if CONFIG_I2C_LOCKED
*old = network_status_update_bool(old, "is_i2c_locked", true);
#else

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -26,6 +26,20 @@ declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
@@ -54,6 +68,34 @@ declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;

View File

@@ -1,3 +1,4 @@
declare const PORT: 9100;
import HtmlWebPackPlugin = require("html-webpack-plugin");
export namespace entry {
const test: string;
@@ -17,7 +18,7 @@ export namespace devServer {
}
export const open: boolean;
export const compress: boolean;
export const port: number;
export { PORT as port };
export const host: string;
export const allowedHosts: string;
export const headers: {
@@ -33,3 +34,4 @@ export namespace devServer {
export function onBeforeSetupMiddleware(devServer: any): void;
}
export const plugins: HtmlWebPackPlugin[];
export {};

View File

@@ -23,5 +23,6 @@
"mock_plugin_has_proxy": "x",
"mock_fail_fw_update":"",
"mock_fail_recovery":"",
"mock_old_recovery":""
"mock_old_recovery":"",
"depth": 16
}

View File

@@ -8,6 +8,7 @@
"scripts": {
"prod": "webpack serve --open --mode=production",
"build": "webpack --mode=production ",
"stats": "webpack --env ANALYZE_SIZE=1 --mode=production ",
"watch": "webpack --progress --watch --mode=development ",
"dev": "webpack serve --open --mode=development"
},
@@ -51,6 +52,7 @@
"lodash-webpack-plugin": "^0.11.6",
"mini-css-extract-plugin": "^2.5.2",
"node-sass": "^7.0.1",
"open": "^9.1.0",
"postcss": "^8.4.5",
"postcss-loader": "^6.2.1",
"purgecss-webpack-plugin": "^4.1.3",
@@ -65,7 +67,7 @@
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"webpack": "^5.67.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.3"
},
@@ -74,7 +76,9 @@
"async-mutex": "^0.3.2",
"bootstrap": "^5.1.3",
"jquery": "^3.6.0",
"popper.js": "^1.16.1"
"popper.js": "^1.16.1",
"webpack-visualizer-plugin": "^0.1.11",
"webpack-visualizer-plugin2": "^1.0.0"
},
"keywords": [
"webppack4",

View File

@@ -44,19 +44,23 @@
</div>
<div class="info navbar-right" style="display: inline-flex;">
<span class="recovery_element material-icons " style="color:orange; display: none" aria-label="🛑" >system_update_alt</span>
<span id="battery" class="material-icons" style="fill:white; display: none" aria-label="🔋" >battery_full</span>
<span class="recovery_element material-icons " style="color:orange; display: none"
aria-label="🛑">system_update_alt</span>
<span id="battery" class="material-icons" style="fill:white; display: none"
aria-label="🔋">battery_full</span>
<span id="o_jack" class="material-icons" style="fill:white; display: none" aria-label="🎧">headphones</span>
<span id="s_airplay" class="material-icons" style="fill:white; display: none" aria-label="🍎">airplay</span>
<em id="s_cspot" class="fab fa-spotify" style="fill:white; display: inline"></em>
<span data-bs-toggle="tooltip" id="o_type" data-bs-placement="top" title="">
<span id="o_bt" class="material-icons" style="fill:white; display: none" aria-label="">bluetooth</span>
<span id="o_spdif" class="material-icons" style="fill:white; display: none" aria-label="">graphic_eq</span>
<span id="o_spdif" class="material-icons" style="fill:white; display: none"
aria-label="">graphic_eq</span>
<span id="o_i2s" class="material-icons" style="fill:white; display: none" aria-label="🔈">speaker</span>
</span>
<span id="ethernet" class="material-icons if_eth" style="fill:white; display: none" aria-label="ETH">cable</span>
<span id="wifiStsIcon" class="material-icons if_wifi"
style="fill:white; display: none" aria-label=""></span>
<span id="ethernet" class="material-icons if_eth" style="fill:white; display: none"
aria-label="ETH">cable</span>
<span id="wifiStsIcon" class="material-icons if_wifi" style="fill:white; display: none"
aria-label=""></span>
</div>
</header>
@@ -215,33 +219,116 @@
<div class="card text-white mb-3">
<div class="card-header">Usage Templates</div>
<div class="card-body">
<fieldset>
<fieldset class="form-group" id="output-tmpl">
<legend>Output</legend>
<div class="form-check">
<label>Output</label><br>
<div class="form-check form-check-inline">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="output-tmpl" id="i2s">
I2S Dac
</label>
</div>
<div class="form-check">
<div class="form-check form-check-inline">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="output-tmpl" id="spdif">
SPDIF
</label>
</div>
<div class="form-check">
<div class="form-check form-check-inline">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="output-tmpl" id="bt">
Bluetooth
</label>
</div>
</fieldset>
<div class="form-group"><label for="player">Player Name</label><input type="text"
class="form-control " placeholder="Squeezelite" id="player"></div>
<div class="form-group"><label for="optional">Optional setting (e.g. for LMS IP
address)</label><input type="text" class="form-control" id="optional"></div>
<fieldset>
<div id="options">
<div class="form-group"><label for="cmd_opt_n">Set the player name</label><input
type="text" class="form-control sqcmd" placeholder="name" id="cmd_opt_n"></div>
<div class="form-group"><label for="cmd_opt_s">Server</label><input type="text"
class="form-control sqcmd" placeholder="server[:port]" id="cmd_opt_s"></div>
<div class="form-group"><label for="cmd_opt_b">Stream and Output buffer sizes (in
Kbytes)</label><input type="text" class="form-control sqcmd"
placeholder="stream:output" id="cmd_opt_b"></div>
<div class="form-group"><label for="cmd_opt_c">Restrict codecs </label><input
type="text" class="form-control sqcmd" placeholder="codec1,codec2"
id="cmd_opt_c"><small class="form-text text-muted">Supported: flac,pcm,mp3,ogg
(mad,mpg for specific mp3 codec)</small></div>
<div class="form-group"><label for="cmd_opt_C">Ouput device close timeout</label><input
type="text" class="form-control sqcmd" placeholder="timeout"
id="cmd_opt_C"><small class="form-text text-muted">Close output device after
timeout seconds, default
is to keep it open while player is 'on'</small></div>
<div class="form-group"><label for="cmd_opt_d">Set logging level</label><input
type="text" class="form-control sqcmd" placeholder="log=level"
id="cmd_opt_d"><small class="form-text text-muted">Logs:
all|slimproto|stream|decode|output, level:
info|debug|sdebug</small></div>
<div class="form-group"><label for="cmd_opt_e">Explicitly exclude native support of one
or more codecs</label><input type="text" class="form-control sqcmd"
placeholder="codec1,codec2" id="cmd_opt_e"><small
class="form-text text-muted">Supported: flac,pcm,mp3,ogg (mad,mpg for specific
mp3 codec)</small></div>
<div class="form-group"><label for="cmd_opt_m">Set mac address</label><input type="text"
class="form-control sqcmd" placeholder="mac addr" id="cmd_opt_m"><small
class="form-text text-muted">Format: ab:cd:ef:12:34:56</small></div>
<div class="form-group"><label for="cmd_opt_r">Sample rates supported, allows output to
be off when squeezelite is started</label><input type="text"
class="form-control sqcmd" placeholder="rates" id="cmd_opt_r"><small
class="form-text text-muted">&lt;maxrate&gt;|&lt;minrate&gt;&lt;maxrate&gt;&lt;rate1&gt;&lt;rate2&gt;&lt;rate3&gt;</small>
</div>
<div class="form-group hide" id="cmd_opt_R">
<label>Resample</label><br>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_none"
suffix="" checked aint="false">
<label class="form-check-label" for="resampleNone">No resampling</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample"
suffix=' -R' aint="false">
<label class="form-check-label" for="resampleNone">Default</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_b"
suffix=' -R -u b' aint="true">
<label class="form-check-label" for="resampleBasic">Basic linear
interpolation</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_l"
suffix=' -R -u l' aint="true">
<label class="form-check-label" for="resample13Taps">13 taps</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_m"
suffix=' -R -u m' aint="true">
<label class="form-check-label" for="resample21Taps">21 taps</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="interpolate"
id="resample_i" suffix=":i">
<label class="form-check-label" for="interpolate">Interpolate filter
coefficients</label>
</div>
</div>
<div class="form-group"><label for="cmd_opt_Z">Report rate to server in helo as the
maximum sample rate we can support</label><input type="text"
class="form-control" placeholder="rate" id="cmd_opt_Z"></div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="cmd_opt_W" value=""
checked="">
Read wave and aiff format from header, ignore server parameters
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="disable-squeezelite"

View File

@@ -28,71 +28,95 @@ Object.assign(Date.prototype, {
return this.toLocaleString(undefined, opt);
},
});
function handleNVSVisible(){
function get_control_option_value(obj) {
let ctrl,id,val,opt;
let radio = false;
let checked = false;
if (typeof (obj) === 'string') {
id = obj;
ctrl = $(`#${id}`);
} else {
id = $(obj).attr('id');
ctrl = $(obj);
}
if(ctrl.attr('type') === 'checkbox'){
opt = $(obj).checked?id.replace('cmd_opt_', ''):'';
val = true;
}
else {
opt = id.replace('cmd_opt_', '');
val = $(obj).val();
val = `${val.includes(" ") ? '"' : ''}${val}${val.includes(" ") ? '"' : ''}`;
}
return { opt, val };
}
function handleNVSVisible() {
let nvs_previous_checked = isEnabled(Cookies.get("show-nvs"));
$('input#show-nvs')[0].checked = nvs_previous_checked ;
$('input#show-nvs')[0].checked = nvs_previous_checked;
if ($('input#show-nvs')[0].checked || recovery) {
$('*[href*="-nvs"]').show();
} else {
$('*[href*="-nvs"]').hide();
$('*[href*="-nvs"]').show();
} else {
$('*[href*="-nvs"]').hide();
}
}
function concatenateOptions(options) {
let commandLine = ' ';
for (const [option, value] of Object.entries(options)) {
if (option !== 'n' && option !== 'o') {
commandLine += `-${option} `;
if (value !== true) {
commandLine += `${value} `;
}
}
}
return commandLine;
}
function isEnabled(val) {
return val!=undefined && typeof val === 'string' && val.match("[Yy1]");
return val != undefined && typeof val === 'string' && val.match("[Yy1]");
}
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: {'label':'','icon': 'media_bluetooth_on'},
bt_disconnected: {'label':'','icon': 'media_bluetooth_off'},
bt_neutral: {'label':'','icon': 'bluetooth'},
bt_connecting: {'label':'','icon': 'bluetooth_searching'},
bt_connected: {'label':'','icon': 'bluetooth_connected'},
bt_disabled: {'label':'','icon': 'bluetooth_disabled'},
play_arrow: {'label':'','icon': 'play_circle_filled'},
pause: {'label':'','icon': 'pause_circle'},
stop: {'label':'','icon': 'stop_circle'},
'': {'label':'','icon':''}
bt_playing: { 'label': '', 'icon': 'media_bluetooth_on' },
bt_disconnected: { 'label': '', 'icon': 'media_bluetooth_off' },
bt_neutral: { 'label': '', 'icon': 'bluetooth' },
bt_connecting: { 'label': '', 'icon': 'bluetooth_searching' },
bt_connected: { 'label': '', 'icon': 'bluetooth_connected' },
bt_disabled: { 'label': '', 'icon': 'bluetooth_disabled' },
play_arrow: { 'label': '', 'icon': 'play_circle_filled' },
pause: { 'label': '', 'icon': 'pause_circle' },
stop: { 'label': '', 'icon': 'stop_circle' },
'': { 'label': '', 'icon': '' }
};
const batIcons = [
{ icon: "battery_0_bar", label:'▪', ranges: [{ f: 5.8, t: 6.8 }, { f: 8.8, t: 10.2 }] },
{ icon: "battery_2_bar", label:'▪▪', ranges: [{ f: 6.8, t: 7.4 }, { f: 10.2, t: 11.1 }] },
{ icon: "battery_3_bar", label:'▪▪▪', ranges: [{ f: 7.4, t: 7.5 }, { f: 11.1, t: 11.25 }] },
{ icon: "battery_4_bar", label:'▪▪▪▪', ranges: [{ f: 7.5, t: 7.8 }, { f: 11.25, t: 11.7 }] }
{ icon: "battery_0_bar", label: '▪', ranges: [{ f: 5.8, t: 6.8 }, { f: 8.8, t: 10.2 }] },
{ icon: "battery_2_bar", label: '▪▪', ranges: [{ f: 6.8, t: 7.4 }, { f: 10.2, t: 11.1 }] },
{ icon: "battery_3_bar", label: '▪▪▪', ranges: [{ f: 7.4, t: 7.5 }, { f: 11.1, t: 11.25 }] },
{ icon: "battery_4_bar", label: '▪▪▪▪', ranges: [{ f: 7.5, t: 7.8 }, { f: 11.25, t: 11.7 }] }
];
const btStateIcons = [
{ desc: 'Idle', sub: ['bt_neutral'] },
@@ -162,7 +186,7 @@ let flashState = {
$('.flact').prop('disabled', false);
$('#flashfilename').value = null;
$('#fw-url-input').value = null;
if(!this.isStateError()){
if (!this.isStateError()) {
$('span#flash-status').html('');
$('#fwProgressLabel').parent().removeClass('bg-danger');
}
@@ -357,8 +381,8 @@ let flashState = {
const xhttp = new XMLHttpRequest();
xhttp.context = this;
var boundHandleUploadProgressEvent = this.HandleUploadProgressEvent.bind(this);
var boundsetOTAError=this.setOTAError.bind(this);
xhttp.upload.addEventListener("progress",boundHandleUploadProgressEvent, false);
var boundsetOTAError = this.setOTAError.bind(this);
xhttp.upload.addEventListener("progress", boundHandleUploadProgressEvent, false);
xhttp.onreadystatechange = function () {
if (xhttp.readyState === 4) {
if (xhttp.status === 0 || xhttp.status === 404) {
@@ -464,11 +488,89 @@ window.handleReboot = function (link) {
$('#reboot_nav').removeClass('active'); delayReboot(500, '', link);
}
}
function isConnected(){
return ConnectedTo.hasOwnProperty('ip') && ConnectedTo.ip!='0.0.0.0'&& ConnectedTo.ip!='';
function parseSqueezeliteCommandLine(commandLine) {
const options = {};
let output, name;
let otherValues = '';
const argRegex = /("[^"]+"|'[^']+'|\S+)/g;
const args = commandLine.match(argRegex);
let i = 0;
while (i < args.length) {
const arg = args[i];
if (arg.startsWith('-')) {
const option = arg.slice(1);
if (option === '') {
otherValues += args.slice(i).join(' ');
break;
}
let value = true;
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
value = args[i + 1].replace(/"/g, '').replace(/'/g, '');
i++;
}
options[option] = value;
} else {
otherValues += arg + ' ';
}
i++;
}
otherValues = otherValues.trim();
output = getOutput(options);
name = getName(options);
let otherOptions={btname:null,n:null};
// assign o and n options to otheroptions if present
if (options.o && output.toUpperCase() === 'BT') {
let temp = parseSqueezeliteCommandLine(options.o);
if(temp.name) {
otherOptions.btname = temp.name;
}
delete options.o;
}
if (options.n) {
otherOptions['n'] = options.n;
delete options.n;
}
return { name, output, options, otherValues,otherOptions };
}
function getIcon(icons){
return isConnected()?icons.icon:icons.label;
function getOutput(options) {
let output;
if (options.o){
output = options.o.replace(/"/g, '').replace(/'/g, '');
/* set output as the first alphanumerical word in the command line */
if (output.indexOf(' ') > 0) {
output = output.substring(0, output.indexOf(' '));
}
}
return output;
}
function getName(options) {
let name;
/* if n option present, assign to name variable */
if (options.n){
name = options.n.replace(/"/g, '').replace(/'/g, '');
}
return name;
}
function isConnected() {
return ConnectedTo.hasOwnProperty('ip') && ConnectedTo.ip != '0.0.0.0' && ConnectedTo.ip != '';
}
function getIcon(icons) {
return isConnected() ? icons.icon : icons.label;
}
function handlebtstate(data) {
let icon = '';
@@ -476,7 +578,7 @@ function handlebtstate(data) {
if (data.bt_status !== undefined && data.bt_sub_status !== undefined) {
const iconindex = btStateIcons[data.bt_status].sub[data.bt_sub_status];
if (iconindex) {
icon = btIcons[iconindex];
icon = btIcons[iconindex];
tt = btStateIcons[data.bt_status].desc;
} else {
icon = btIcons.bt_connected;
@@ -485,19 +587,28 @@ function handlebtstate(data) {
}
$('#o_type').attr('title', tt);
$('#o_bt').html(isConnected()?icon.label:icon.text);
$('#o_bt').html(isConnected() ? icon.label : icon.text);
}
function handleTemplateTypeRadio(outtype) {
$('#o_type').children('span').css({ display: 'none' });
let changed = false;
if (outtype === 'bt') {
changed = output !== 'bt' && output !== '';
output = 'bt';
} else if (outtype === 'spdif') {
changed = output !== 'spdif' && output !== '';
output = 'spdif';
} else {
changed = output !== 'i2s' && output !== '';
output = 'i2s';
}
$('#' + output).prop('checked', true);
$('#o_' + output).css({ display: 'inline' });
if (changed) {
Object.keys(commandDefaults[output]).forEach(function (key) {
$(`#cmd_opt_${key}`).val(commandDefaults[output][key]);
});
}
}
function handleExceptionResponse(xhr, _ajaxOptions, thrownError) {
@@ -545,7 +656,17 @@ let releaseURL =
let recovery = false;
let messagesHeld = false;
const commandHeader = 'squeezelite -b 500:2000 -d all=info -C 30 -W';
let commandBTSinkName = '';
const commandHeader = 'squeezelite ';
const commandDefaults = {
i2s: { b: "500:2000", C: "30", W: "", Z: "96000", o: "I2S" },
spdif: { b: "500:2000", C: "30", W: "", Z: "48000", o: "SPDIF" },
bt: { b: "500:2000", C: "30", W: "", Z: "44100", o: "BT" },
};
let validOptions = {
codecs: ['flac', 'pcm', 'mp3', 'ogg', 'aac', 'wma', 'alac', 'dsd', 'mad', 'mpg']
};
//let blockFlashButton = false;
let apList = null;
//let selectedSSID = '';
@@ -559,6 +680,7 @@ let hostName = '';
let versionName = 'Squeezelite-ESP32';
let prevmessage = '';
let project_name = versionName;
let depth = 16;
let board_model = '';
let platform_name = versionName;
let preset_name = '';
@@ -690,11 +812,6 @@ function handleHWPreset(allfields, reboot) {
}
},
});
}
@@ -773,23 +890,34 @@ function delayReboot(duration, cmdname, ota = 'reboot') {
// 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() + '"';
let commandLine = `${commandHeader} -o ${output} `;
$('.sqcmd').each(function () {
let { opt, val } = get_control_option_value($(this));
if ((opt && opt.length>0 ) && typeof(val) == 'boolean' || val.length > 0) {
const optStr=opt===':'?opt:(` -${opt} `);
val = typeof(val) == 'boolean'?'':val;
commandLine += `${optStr} ${val}`;
}
});
const resample=$('#cmd_opt_R input[name=resample]:checked');
if (resample.length>0 && resample.attr('suffix')!=='') {
commandLine += resample.attr('suffix');
// now check resample_i option and if checked, add suffix to command line
if ($('#resample_i').is(":checked") && resample.attr('aint') =='true') {
commandLine += $('#resample_i').attr('suffix');
}
}
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();
}
commandLine += concatenateOptions(options);
const data = {
timestamp: Date.now(),
};
@@ -880,9 +1008,33 @@ window.handleConnect = function () {
// now we can re-set the intervals regardless of result
}
function renderError(opt,error){
const fieldname = `cmd_opt_${opt}`;
let errorFieldName=`${fieldname}-error`;
let errorField=$(`#${errorFieldName}`);
let field=$(`#${fieldname}`);
if (!errorField || errorField.length ==0) {
field.after(`<div id="${errorFieldName}" class="invalid-feedback"></div>`);
errorField=$(`#${errorFieldName}`);
}
if(error.length ==0){
errorField.hide();
field.removeClass('is-invalid');
field.addClass('is-valid');
errorField.text('');
}
else {
errorField.show();
errorField.text(error);
field.removeClass('is-valid');
field.addClass('is-invalid');
}
return errorField;
}
$(document).ready(function () {
$('.material-icons').each(function (_index, entry) {
entry.attributes['icon']=entry.textContent;
entry.attributes['icon'] = entry.textContent;
});
setIcons(true);
handleNVSVisible();
@@ -908,6 +1060,43 @@ $(document).ready(function () {
});
setTimeout(refreshAP, 1500);
/* add validation for cmd_opt_c, which accepts a comma separated list.
getting known codecs from validOptions.codecs array
use bootstrap classes to highlight the error with an overlay message */
$('#options input').on('input', function () {
const { opt, val } = get_control_option_value(this);
if (opt === 'c' || opt === 'e') {
const fieldname = `cmd_opt_${opt}_codec-error`;
const values = val.split(',').map(function (item) {
return item.trim();
});
/* get a list of invalid codecs */
const invalid = values.filter(function (item) {
return !validOptions.codecs.includes(item);
});
renderError(opt,invalid.length > 0 ? `Invalid codec(s) ${invalid.join(', ')}` : '');
}
/* add validation for cmd_opt_m, which accepts a mac_address */
if (opt === 'm') {
const mac_regex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
renderError(opt,mac_regex.test(val) ? '' : 'Invalid MAC address');
}
if (opt === 'r') {
const rateRegex = /^(\d+\.?\d*|\.\d+)-(\d+\.?\d*|\.\d+)$|^(\d+\.?\d*)$|^(\d+\.?\d*,)+\d+\.?\d*$/;
renderError(opt,rateRegex.test(val)?'':`Invalid rate(s) ${val}. Acceptable format: <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>`);
}
}
);
$('#WifiConnectDialog')[0].addEventListener('shown.bs.modal', function (event) {
$("*[class*='connecting']").hide();
@@ -1029,7 +1218,7 @@ $(document).ready(function () {
$('input#show-nvs').on('click', function () {
this.checked = this.checked ? 1 : 0;
Cookies.set("show-nvs", this.checked?'Y':'N');
Cookies.set("show-nvs", this.checked ? 'Y' : 'N');
handleNVSVisible();
});
$('#btn_reboot_recovery').on('click', function () {
@@ -1048,7 +1237,7 @@ $(document).ready(function () {
saveAutoexec1(true);
});
$('#btn_disconnect').on('click', function () {
ConnectedTo={};
ConnectedTo = {};
refreshAPHTML2();
$.ajax({
url: '/connect.json',
@@ -1303,15 +1492,15 @@ window.setURL = function (button) {
function rssiToIcon(rssi) {
if (rssi >= -55) {
return {'label':'****','icon':`signal_wifi_statusbar_4_bar`};
return { 'label': '****', 'icon': `signal_wifi_statusbar_4_bar` };
} else if (rssi >= -60) {
return {'label':'***','icon':`network_wifi_3_bar`};
return { 'label': '***', 'icon': `network_wifi_3_bar` };
} else if (rssi >= -65) {
return {'label':'**','icon':`network_wifi_2_bar`};
return { 'label': '**', 'icon': `network_wifi_2_bar` };
} else if (rssi >= -70) {
return {'label':'*','icon':`network_wifi_1_bar`};
return { 'label': '*', 'icon': `network_wifi_1_bar` };
} else {
return {'label':'.','icon':`signal_wifi_statusbar_null`};
return { 'label': '.', 'icon': `signal_wifi_statusbar_null` };
}
}
@@ -1339,9 +1528,9 @@ function refreshAP() {
});
}
function formatAP(ssid, rssi, auth) {
const rssi_icon=rssiToIcon(rssi);
const auth_icon={label:auth == 0 ? '🔓' : '🔒',icon:auth == 0 ? 'no_encryption' : 'lock'};
const rssi_icon = rssiToIcon(rssi);
const auth_icon = { label: auth == 0 ? '🔓' : '🔒', icon: auth == 0 ? 'no_encryption' : 'lock' };
return `<tr data-bs-toggle="modal" data-bs-target="#WifiConnectDialog"><td></td><td>${ssid}</td><td>
<span class="material-icons" style="fill:white; display: inline" aria-label="${rssi_icon.label}" icon="${rssi_icon.icon}" >${getIcon(rssi_icon)}</span>
</td><td>
@@ -1512,9 +1701,9 @@ function getMessages() {
break;
}
}
setTimeout(getMessages,messageInterval);
setTimeout(getMessages, messageInterval);
}).fail(function (xhr, ajaxOptions, thrownError) {
if (xhr.status == 404) {
$('.orec').hide(); // system commands won't be available either
messagesHeld = true;
@@ -1522,15 +1711,15 @@ function getMessages() {
else {
handleExceptionResponse(xhr, ajaxOptions, thrownError);
}
if(xhr.status == 0 && xhr.readyState ==0){
if (xhr.status == 0 && xhr.readyState == 0) {
// probably a timeout. Target is rebooting?
setTimeout(getMessages,messageInterval*2); // increase duration if a failure happens
setTimeout(getMessages, messageInterval * 2); // increase duration if a failure happens
}
else if(!messagesHeld){
else if (!messagesHeld) {
// 404 here means we rebooted to an old recovery
setTimeout(getMessages,messageInterval); // increase duration if a failure happens
setTimeout(getMessages, messageInterval); // increase duration if a failure happens
}
}
);
@@ -1552,18 +1741,18 @@ function handleRecoveryMode(data) {
$('#boot-button').html('Reboot');
$('#boot-form').attr('action', '/reboot_ota.json');
} else {
if(!recovery && messagesHeld){
messagesHeld=false;
setTimeout(getMessages,messageInterval); // increase duration if a failure happens
if (!recovery && messagesHeld) {
messagesHeld = false;
setTimeout(getMessages, messageInterval); // increase duration if a failure happens
}
recovery = false;
$('.recovery_element').hide();
$('.ota_element').show();
$('#boot-button').html('Recovery');
$('#boot-form').attr('action', '/recovery.json');
}
}
function hasConnectionChanged(data) {
@@ -1645,9 +1834,9 @@ function handleWifiDialog(data) {
}
}
function setIcons(offline){
function setIcons(offline) {
$('.material-icons').each(function (_index, entry) {
entry.textContent = entry.attributes[offline?'aria-label':'icon'].value;
entry.textContent = entry.attributes[offline ? 'aria-label' : 'icon'].value;
});
}
function handleNetworkStatus(data) {
@@ -1682,13 +1871,13 @@ function batteryToIcon(voltage) {
for (const iconEntry of batIcons) {
for (const entryRanges of iconEntry.ranges) {
if (inRange(voltage, entryRanges.f, entryRanges.t)) {
return { label: iconEntry.label, icon:iconEntry.icon};
return { label: iconEntry.label, icon: iconEntry.icon };
}
}
}
return {label:'▪▪▪▪',icon:"battery_full"};
return { label: '▪▪▪▪', icon: "battery_full" };
}
function checkStatus() {
$.ajaxSetup({
@@ -1700,6 +1889,16 @@ function checkStatus() {
handleNetworkStatus(data);
handlebtstate(data);
flashState.EventTargetStatus(data);
if(data.depth) {
depth = data.depth;
if(depth==16){
$('#cmd_opt_R').show();
}
else{
$('#cmd_opt_R').hide();
}
}
if (data.project_name && data.project_name !== '') {
project_name = data.project_name;
@@ -1717,10 +1916,10 @@ function checkStatus() {
$('span#flash-status').html('');
}
if (data.Voltage) {
const bat_icon=batteryToIcon(data.Voltage);
const bat_icon = batteryToIcon(data.Voltage);
$('#battery').html(`${getIcon(bat_icon)}`);
$('#battery').attr("aria-label",bat_icon.label);
$('#battery').attr("icon",bat_icon.icon);
$('#battery').attr("aria-label", bat_icon.label);
$('#battery').attr("icon", bat_icon.icon);
$('#battery').show();
} else {
$('#battery').hide();
@@ -1757,15 +1956,15 @@ function checkStatus() {
});
}
$('#o_jack').css({ display: Number(data.Jack) ? 'inline' : 'none' });
setTimeout(checkStatus,statusInterval);
setTimeout(checkStatus, statusInterval);
}).fail(function (xhr, ajaxOptions, thrownError) {
handleExceptionResponse(xhr, ajaxOptions, thrownError);
if(xhr.status == 0 && xhr.readyState ==0){
if (xhr.status == 0 && xhr.readyState == 0) {
// probably a timeout. Target is rebooting?
setTimeout(checkStatus,messageInterval*2); // increase duration if a failure happens
setTimeout(checkStatus, messageInterval * 2); // increase duration if a failure happens
}
else {
setTimeout(checkStatus,messageInterval); // increase duration if a failure happens
setTimeout(checkStatus, messageInterval); // increase duration if a failure happens
}
});
}
@@ -1995,6 +2194,7 @@ function getConfig() {
$('#nvsTable tr').remove();
const data = (entries.config ? entries.config : entries);
SystemConfig = data;
commandBTSinkName = '';
Object.keys(data)
.sort()
.forEach(function (key) {
@@ -2006,20 +2206,15 @@ function getConfig() {
$('#disable-squeezelite')[0].checked = false;
}
} else if (key === 'autoexec1') {
const re = /-o\s?(["][^"]*["]|[^-]+)/g;
const m = re.exec(val);
if (m[1].toUpperCase().startsWith('I2S')) {
handleTemplateTypeRadio('i2s');
} else if (m[1].toUpperCase().startsWith('SPDIF')) {
handleTemplateTypeRadio('spdif');
} else if (m[1].toUpperCase().startsWith('"BT')) {
handleTemplateTypeRadio('bt');
}
/* call new function to parse the squeezelite options */
processSqueezeliteCommandLine(val);
} else if (key === 'host_name') {
val = val.replaceAll('"', '');
$('input#dhcp-name1').val(val);
$('input#dhcp-name2').val(val);
$('#player').val(val);
if ($('#cmd_opt_n').length == 0) {
$('#cmd_opt_n').val(val);
}
document.title = val;
hostName = val;
} else if (key === 'rel_api') {
@@ -2054,6 +2249,10 @@ function getConfig() {
);
$('input#' + key).val(data[key].value);
});
if(commandBTSinkName.length > 0) {
// persist the sink name found in the autoexec1 command line
$('#cfg-audio-bt_source-sink_name').val(commandBTSinkName);
}
$('tbody#nvsTable').append(
"<tr><td><input type='text' class='form-control' id='nvs-new-key' placeholder='new key'></td><td><input type='text' class='form-control' id='nvs-new-value' placeholder='new value' nvs_type=33 ></td></tr>"
);
@@ -2083,6 +2282,40 @@ function getConfig() {
handleExceptionResponse(xhr, ajaxOptions, thrownError);
});
}
function processSqueezeliteCommandLine(val) {
const parsed = parseSqueezeliteCommandLine(val);
if (parsed.output.toUpperCase().startsWith('I2S')) {
handleTemplateTypeRadio('i2s');
} else if (parsed.output.toUpperCase().startsWith('SPDIF')) {
handleTemplateTypeRadio('spdif');
} else if (parsed.output.toUpperCase().startsWith('BT')) {
if(parsed.otherOptions.btname){
commandBTSinkName= parsed.otherOptions.btname;
}
handleTemplateTypeRadio('bt');
}
Object.keys(parsed.options).forEach(function (key) {
const option = parsed.options[key];
if (!$(`#cmd_opt_${key}`).hasOwnProperty('checked')) {
$(`#cmd_opt_${key}`).val(option);
} else {
$(`#cmd_opt_${key}`)[0].checked = option;
}
});
if (parsed.options.hasOwnProperty('u')) {
// parse -u v[:i] and check the appropriate radio button with id #resample_v
const [resampleValue, resampleInterpolation] = parsed.options.u.split(':');
$(`#resample_${resampleValue}`).prop('checked', true);
// if resampleinterpolation is set, check resample_i checkbox
if (resampleInterpolation) {
$('#resample_i').prop('checked', true);
}
}
}
function showLocalMessage(message, severity) {
const msg = {
message: message,

View File

@@ -1,5 +1,5 @@
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.60aa97be4459083adfab.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.cd56ff129e3113d8cd3a.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/favicon-32x32.png BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/index.html.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.392dfa.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.392dfa.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.ab1d13.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.ab1d13.bundle.js.gz BINARY)

View File

@@ -1,34 +1,34 @@
// Automatically generated. Do not edit manually!.
#include <inttypes.h>
extern const uint8_t _index_60aa97be4459083adfab_css_gz_start[] asm("_binary_index_60aa97be4459083adfab_css_gz_start");
extern const uint8_t _index_60aa97be4459083adfab_css_gz_end[] asm("_binary_index_60aa97be4459083adfab_css_gz_end");
extern const uint8_t _index_cd56ff129e3113d8cd3a_css_gz_start[] asm("_binary_index_cd56ff129e3113d8cd3a_css_gz_start");
extern const uint8_t _index_cd56ff129e3113d8cd3a_css_gz_end[] asm("_binary_index_cd56ff129e3113d8cd3a_css_gz_end");
extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_start");
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
extern const uint8_t _index_392dfa_bundle_js_gz_start[] asm("_binary_index_392dfa_bundle_js_gz_start");
extern const uint8_t _index_392dfa_bundle_js_gz_end[] asm("_binary_index_392dfa_bundle_js_gz_end");
extern const uint8_t _node_vendors_392dfa_bundle_js_gz_start[] asm("_binary_node_vendors_392dfa_bundle_js_gz_start");
extern const uint8_t _node_vendors_392dfa_bundle_js_gz_end[] asm("_binary_node_vendors_392dfa_bundle_js_gz_end");
extern const uint8_t _index_ab1d13_bundle_js_gz_start[] asm("_binary_index_ab1d13_bundle_js_gz_start");
extern const uint8_t _index_ab1d13_bundle_js_gz_end[] asm("_binary_index_ab1d13_bundle_js_gz_end");
extern const uint8_t _node_vendors_ab1d13_bundle_js_gz_start[] asm("_binary_node_vendors_ab1d13_bundle_js_gz_start");
extern const uint8_t _node_vendors_ab1d13_bundle_js_gz_end[] asm("_binary_node_vendors_ab1d13_bundle_js_gz_end");
const char * resource_lookups[] = {
"/css/index.60aa97be4459083adfab.css.gz",
"/css/index.cd56ff129e3113d8cd3a.css.gz",
"/favicon-32x32.png",
"/index.html.gz",
"/js/index.392dfa.bundle.js.gz",
"/js/node_vendors.392dfa.bundle.js.gz",
"/js/index.ab1d13.bundle.js.gz",
"/js/node_vendors.ab1d13.bundle.js.gz",
""
};
const uint8_t * resource_map_start[] = {
_index_60aa97be4459083adfab_css_gz_start,
_index_cd56ff129e3113d8cd3a_css_gz_start,
_favicon_32x32_png_start,
_index_html_gz_start,
_index_392dfa_bundle_js_gz_start,
_node_vendors_392dfa_bundle_js_gz_start
_index_ab1d13_bundle_js_gz_start,
_node_vendors_ab1d13_bundle_js_gz_start
};
const uint8_t * resource_map_end[] = {
_index_60aa97be4459083adfab_css_gz_end,
_index_cd56ff129e3113d8cd3a_css_gz_end,
_favicon_32x32_png_end,
_index_html_gz_end,
_index_392dfa_bundle_js_gz_end,
_node_vendors_392dfa_bundle_js_gz_end
_index_ab1d13_bundle_js_gz_end,
_node_vendors_ab1d13_bundle_js_gz_end
};

View File

@@ -10,6 +10,7 @@ const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const webpack = require("webpack");
const path = require("path");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const globSync = require("glob").sync;
const glob = require('glob');
const { merge } = require('webpack-merge');
@@ -39,7 +40,15 @@ class BuildEventsHook {
module.exports = (env, options) => (
merge(
env.WEBPACK_SERVE ? devserver : {},
env.ANALYZE_SIZE?{ plugins: [ new BundleAnalyzerPlugin() ]}:{},
env.ANALYZE_SIZE?{ plugins: [ new BundleAnalyzerPlugin(
{
analyzerMode: 'static',
generateStatsFile: true,
statsFilename: 'stats.json',
}
) ]}:{},
{
entry:
{
@@ -170,7 +179,6 @@ module.exports = (env, options) => (
],
},
plugins: [
new HtmlWebpackPlugin({
title: 'SqueezeESP32',
template: './src/index.ejs',
@@ -328,6 +336,7 @@ extern const uint8_t * resource_map_end[];`;
optimization: {
minimize: true,
providedExports: true,
usedExports: true,
minimizer: [
new TerserPlugin({
@@ -340,7 +349,14 @@ extern const uint8_t * resource_map_end[];`;
// enable parallel running
parallel: true,
}),
new HtmlMinimizerPlugin(),
new HtmlMinimizerPlugin({
minimizerOptions: {
removeComments: true,
removeOptionalTags: true,
}
}
),
new CssMinimizerPlugin(),
new ImageMinimizerPlugin({
minimizer: {

View File

@@ -1,6 +1,6 @@
/***********************************
webpack_headers
dist/css/index.60aa97be4459083adfab.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.392dfa.bundle.js.gz,dist/js/node_vendors.392dfa.bundle.js.gz
dist/css/index.cd56ff129e3113d8cd3a.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.ab1d13.bundle.js.gz,dist/js/node_vendors.ab1d13.bundle.js.gz
***********************************/
#pragma once
#include <inttypes.h>

View File

@@ -6,6 +6,7 @@ const HtmlWebPackPlugin = require('html-webpack-plugin');
const { Command } = require('commander');
let cmdLines= { };
var { parseArgsStringToArgv } = require('string-argv');
const PORT = 9100;
const data = {
messages: require("../mock/messages.json"),
@@ -133,11 +134,10 @@ const connectReturnCode = {
}
module.exports ={
entry: {
test: './src/test.ts',
test: './src/test.ts'
},
devServer: {
static: {
directory: path.resolve(__dirname, './dist'),
staticOptions: {},
@@ -158,11 +158,10 @@ module.exports ={
},
open: true,
compress: true,
port: 9100,
port: PORT,
host: '127.0.0.1',//your ip address
allowedHosts: "all",
headers: {'Access-Control-Allow-Origin': '*',
'Accept-Encoding': 'identity'},
headers: {'Access-Control-Allow-Origin': '*', 'Accept-Encoding': 'identity'},
client: {
logging: "verbose",
// Can be used only for `errors`/`warnings`
@@ -179,8 +178,18 @@ module.exports ={
throw new Error('webpack-dev-server is not defined');
}
const port = devServer.server.address().port;
console.log('Listening on port:', port);
const PORT = devServer.server.address().port;
// get the path to the test page
const compiler = devServer.compiler;
const entry = compiler.options.entry;
const testEntry = entry['test'].import[0];
const testPath = testEntry.replace('./src/', '').replace('.ts', '.html');
// open the test page
import('open').then((open) => open.default(`http://localhost:${PORT}/${testPath}`));
},
onBeforeSetupMiddleware: function (devServer) {