diff --git a/base.php b/base.php index c0458cf..e31c847 100644 --- a/base.php +++ b/base.php @@ -97,7 +97,11 @@ function je($s) function tr($key) { global $_messages; - return isset($_messages[$key]) ? $_messages[$key] : ('??' . $key . '??'); + if (isset($_messages[$key])) return $_messages[$key]; + else { + ob_end_clean(); + die('??' . $key . '??'); + } } /** Like eval, but allows */ diff --git a/compile_html.php b/compile_html.php index 5bc4f15..c8698fc 100755 --- a/compile_html.php +++ b/compile_html.php @@ -22,9 +22,22 @@ ob_start(); foreach($_pages as $_k => $p) { if ($p->bodyclass == 'api') { if (ESP_DEMO) { - $target = 'term.html'; - echo "Generating: ~$_k.html -> $target\n"; - $s = ""; + echo "Generating: ~$_k.html (bounce)\n"; + + if ($_k=='index') { + $s = ""; + } + else { + $s = " + "; + } + + } else { continue; } diff --git a/js/appcommon.js b/js/appcommon.js index 26f8733..0f103df 100644 --- a/js/appcommon.js +++ b/js/appcommon.js @@ -33,7 +33,11 @@ $.ready(function () { let h = x.querySelector('h2') let hdl = function () { - $(x).toggleClass('expanded') + if ($(x).hasClass('d-expanded')) { + $(x).removeClass('d-expanded') + } else { + $(x).toggleClass('expanded') + } } $(h).on('click', hdl).on('keypress', cr(hdl)) }) @@ -90,24 +94,39 @@ $.ready(function () { // (a way to pass errors back from server via redirect) let errAt = window.location.search.indexOf('err=') if (errAt !== -1 && qs('.Box.errors')) { - let errs = window.location.search.substr(errAt + 4).split(',') + let errs = decodeURIComponent(window.location.search.substr(errAt + 4)).split(',') let humanReadableErrors = [] errs.forEach(function (er) { let lbls = qsa('label[for="' + er + '"]') - for (let i = 0; i < lbls.length; i++) { - let lbl = lbls[i] - lbl.classList.add('error') - if (i === 0) humanReadableErrors.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, '')) + if (lbls) { + for (let i = 0; i < lbls.length; i++) { + let lbl = lbls[i] + lbl.classList.add('error') + if (i === 0) humanReadableErrors.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, '')) + } + } else { + humanReadableErrors.push(er) } - // else { - // hres.push(er) - // } }) qs('.Box.errors .list').innerHTML = humanReadableErrors.join(', ') qs('.Box.errors').classList.remove('hidden') } + let msgAt = window.location.search.indexOf('msg=') + if (msgAt !== -1 && qs('.Box.message')) { + let msg = decodeURIComponent(window.location.search.substr(msgAt + 4)) + let box = qs('.Box.message') + box.innerHTML = msg + box.classList.remove('hidden') + setTimeout(() => { + box.classList.add('hiding') + setTimeout(() => { + box.classList.add('hidden') + }, 1000) + }, 2000) + } + modal.init() notify.init() diff --git a/js/term/connection.js b/js/term/connection.js index 7b799ca..8e8f7be 100644 --- a/js/term/connection.js +++ b/js/term/connection.js @@ -19,12 +19,18 @@ module.exports = class TermConnection extends EventEmitter { this.pageShown = false + this.disconnectTimeout = null + document.addEventListener('visibilitychange', () => { if (document.hidden === true) { console.info('Window lost focus, freeing socket') - this.closeSocket() - clearTimeout(this.heartbeatTimeout) + // Delayed, avoid disconnecting if the background time is short + this.disconnectTimeout = setTimeout(() => { + this.closeSocket() + clearTimeout(this.heartbeatTimeout) + }, 1000) } else { + clearTimeout(this.disconnectTimeout) console.info('Window got focus, re-connecting') this.init() } @@ -80,7 +86,6 @@ module.exports = class TermConnection extends EventEmitter { break default: - this.emit('load') this.screen.load(evt.data) if (!this.pageShown) { window.showPage() @@ -118,7 +123,7 @@ module.exports = class TermConnection extends EventEmitter { console.error('Socket not ready') return false } - if (typeof message != 'string') { + if (typeof message !== 'string') { message = JSON.stringify(message) } this.ws.send(message) @@ -161,7 +166,7 @@ module.exports = class TermConnection extends EventEmitter { heartbeat () { clearTimeout(this.heartbeatTimeout) - this.heartbeatTimeout = setTimeout(() => this.onHeartbeatFail(), 2000) + this.heartbeatTimeout = setTimeout(() => this.onHeartbeatFail(), 2500) } onHeartbeatFail () { diff --git a/js/term/index.js b/js/term/index.js index 3a7a6c1..41593ac 100644 --- a/js/term/index.js +++ b/js/term/index.js @@ -14,33 +14,47 @@ module.exports = function (opts) { const input = TermInput(conn, screen) const termUpload = TermUpload(conn, input, screen) screen.input = input + screen.conn = conn input.termUpload = termUpload - // we delay the display of "connecting" to avoid flash when changing tabs with the terminal open - let showConnectingTimeout = -1 + let showSplashTimeout = null + let showSplash = (obj, delay = 250) => { + clearTimeout(showSplashTimeout) + showSplashTimeout = setTimeout(() => { + screen.window.statusScreen = obj + }, delay) + } + conn.on('open', () => { - showConnectingTimeout = setTimeout(() => { - screen.window.statusScreen = { title: 'Connecting', loading: true } - }, 250) + // console.log('*open') + showSplash({ title: 'Connecting', loading: true }) }) conn.on('connect', () => { - clearTimeout(showConnectingTimeout) - screen.window.statusScreen = { title: 'Waiting for content', loading: true } + // console.log('*connect') + showSplash({ title: 'Waiting for content', loading: true }) }) conn.on('load', () => { + // console.log('*load') + clearTimeout(showSplashTimeout) if (screen.window.statusScreen) screen.window.statusScreen = null }) conn.on('disconnect', () => { - clearTimeout(showConnectingTimeout) - screen.window.statusScreen = { title: 'Disconnected' } + // console.log('*disconnect') + showSplash({ title: 'Disconnected' }) screen.screen = [] screen.screenFG = [] screen.screenBG = [] screen.screenAttrs = [] }) - conn.on('silence', () => { screen.window.statusScreen = { title: 'Waiting for server', loading: true } }) + conn.on('silence', () => { + // console.log('*silence') + showSplash({ title: 'Waiting for server', loading: true }, 0) + }) // conn.on('ping-fail', () => { screen.window.statusScreen = { title: 'Disconnected' } }) - conn.on('ping-success', () => { screen.window.statusScreen = { title: 'Re-connecting', loading: true } }) + conn.on('ping-success', () => { + // console.log('*ping-success') + showSplash({ title: 'Re-connecting', loading: true }, 0) + }) conn.init() input.init(opts) diff --git a/js/term/screen.js b/js/term/screen.js index e71a7e3..b24ce98 100644 --- a/js/term/screen.js +++ b/js/term/screen.js @@ -30,6 +30,15 @@ module.exports = class TermScreen extends EventEmitter { return () => console.warn('TermScreen#input not set!') } }) + // dummy. Handle for Conn + this.conn = new Proxy({}, { + get () { + return () => console.warn('TermScreen#conn not set!') + }, + set (a, b) { + return () => console.warn('TermScreen#conn not set!') + } + }) this.cursor = { x: 0, diff --git a/js/term/screen_parser.js b/js/term/screen_parser.js index 9e83e12..c210e00 100644 --- a/js/term/screen_parser.js +++ b/js/term/screen_parser.js @@ -199,7 +199,7 @@ module.exports = class ScreenParser { if (this.screen.window.debug) console.log(`Blinky cells: ${this.screen.blinkingCellCount}`) this.screen.renderer.scheduleDraw('load', 16) - this.screen.emit('load') + this.screen.conn.emit('load') } /** diff --git a/js/wifi.js b/js/wifi.js index 40d8020..9728a6f 100644 --- a/js/wifi.js +++ b/js/wifi.js @@ -7,8 +7,8 @@ const tr = require('./lang') let curSSID // Get XX % for a slider input - function rangePt (inp) { - return Math.round(((inp.value / inp.max) * 100)) + '%' + function calc_dBm (inp) { + return `+${(inp.value * 0.25).toFixed(2)} dBm` } // Display selected STA SSID etc @@ -142,11 +142,11 @@ const tr = require('./lang') let inp = x.querySelector('input') let disp1 = x.querySelector('.x-disp1') let disp2 = x.querySelector('.x-disp2') - let t = rangePt(inp) + let t = calc_dBm(inp) $(disp1).html(t) $(disp2).html(t) $(inp).on('input', function () { - t = rangePt(inp) + t = calc_dBm(inp) $(disp1).html(t) $(disp2).html(t) }) diff --git a/lang/en.php b/lang/en.php index 9b29c99..9333add 100644 --- a/lang/en.php +++ b/lang/en.php @@ -12,10 +12,24 @@ return [ 'menu.term' => 'Back to Terminal', 'menu.cfg_system' => 'System Settings', 'menu.cfg_wifi_conn' => 'Connecting to Network', - 'menu.settings' => 'Settings', - 'title.term' => 'Terminal', + // not used - api etc. Added to suppress warnings + 'menu.term_set' => '', + 'menu.wifi_connstatus' => '', + 'menu.wifi_set' => '', + 'menu.wifi_scan' => '', + 'menu.network_set' => '', + 'menu.system_set' => '', + 'menu.write_defaults' => '', + 'menu.restore_defaults' => '', + 'menu.restore_hard' => '', + 'menu.reset_screen' => '', + 'menu.index' => '', + + // Terminal page + + 'title.term' => 'Terminal', // page title of the terminal page 'term_nav.config' => 'Config', 'term_nav.wifi' => 'WiFi', @@ -26,18 +40,14 @@ return [ 'term_nav.keybd' => 'Keyboard', 'term_nav.paste_prompt' => 'Paste text to send:', - 'net.ap' => 'DHCP Server (AP)', - 'net.sta' => 'DHCP Client (Station)', - 'net.sta_mac' => 'Station MAC', - 'net.ap_mac' => 'AP MAC', - 'net.details' => 'MAC addresses', + // Terminal settings page 'term.defaults' => 'Initial Settings', 'term.expert' => 'Expert Options', 'term.explain_initials' => ' - Those are the initial settings used after ESPTerm powers on or when the screen - reset command is received. Some options can be changed by the application via escape sequences, - those changes won\'t be saved in Flash. + Those are the initial settings used after ESPTerm powers on, or when the screen + reset command is received (\ec). They can be changed by the + terminal application using escape sequences. ', 'term.explain_expert' => ' Those are advanced config options that usually don\'t need to be changed. @@ -58,7 +68,7 @@ return [ 'term.term_width' => 'Width', 'term.term_height' => 'Height', 'term.buttons' => 'Button labels', - 'term.theme' => 'Color scheme', + 'term.theme' => 'Color palette', 'term.cursor_shape' => 'Cursor style', 'term.parser_tout_ms' => 'Parser timeout', 'term.display_tout_ms' => 'Redraw delay', @@ -66,14 +76,14 @@ return [ 'term.fn_alt_mode' => 'SS3 Fn keys', 'term.show_config_links' => 'Show nav links', 'term.show_buttons' => 'Show buttons', - 'term.loopback' => 'Local Echo', - 'term.crlf_mode' => 'Enter sends CR+LF', + 'term.loopback' => 'Local Echo (SRM)', + 'term.crlf_mode' => 'Enter = CR+LF (LNM)', 'term.want_all_fn' => 'Capture all keys
(F5, F11, F12…)', 'term.button_msgs' => 'Button codes
(ASCII, dec, CSV)', 'term.color_fg' => 'Default fg.', 'term.color_bg' => 'Default bg.', - 'term.color_fg_prev' => 'Fg. colors', - 'term.color_bg_prev' => 'Bg. colors', + 'term.color_fg_prev' => 'Foreground', + 'term.color_bg_prev' => 'Background', 'term.colors_preview' => 'Defaults', 'cursor.block_blink' => 'Block, blinking', @@ -83,23 +93,7 @@ return [ 'cursor.bar_blink' => 'I-bar, blinking', 'cursor.bar_steady' => 'I-bar, steady', -// // terminal color labels -// 'color.0' => 'Black', -// 'color.1' => 'Red', -// 'color.2' => 'Green', -// 'color.3' => 'Yellow', -// 'color.4' => 'Blue', -// 'color.5' => 'Purple', -// 'color.6' => 'Cyan', -// 'color.7' => 'Silver', -// 'color.8' => 'Gray', -// 'color.9' => 'Light Red', -// 'color.10' => 'Light Green', -// 'color.11' => 'Light Yellow', -// 'color.12' => 'Light Blue', -// 'color.13' => 'Light Purple', -// 'color.14' => 'Light Cyan', -// 'color.15' => 'White', + // Network config page 'net.explain_sta' => ' Switch off Dynamic IP to configure the static IP address.', @@ -118,6 +112,14 @@ return [ 'net.sta_addr_mask' => 'Subnet mask', 'net.sta_addr_gw' => 'Gateway IP', + 'net.ap' => 'DHCP Server (AP)', + 'net.sta' => 'DHCP Client (Station)', + 'net.sta_mac' => 'Station MAC', + 'net.ap_mac' => 'AP MAC', + 'net.details' => 'MAC addresses', + + // Wifi config page + 'wifi.ap' => 'Built-in Access Point', 'wifi.sta' => 'Join Existing Network', @@ -143,76 +145,89 @@ return [ 'wifi.enter_passwd' => 'Enter password for ":ssid:"', 'wifi.sta_explain' => 'After selecting a network, press Apply to connect.', - 'wifi.conn.status' => 'Status:', - 'wifi.conn.back_to_config' => 'Back to WiFi config', - 'wifi.conn.telemetry_lost' => 'Telemetry lost; something went wrong, or your device disconnected.', - 'wifi.conn.explain_android_sucks' => ' + // Wifi connecting status page + + 'wificonn.status' => 'Status:', + 'wificonn.back_to_config' => 'Back to WiFi config', + 'wificonn.telemetry_lost' => 'Telemetry lost; something went wrong, or your device disconnected.', + 'wificonn.explain_android_sucks' => ' If you\'re configuring ESPTerm via a smartphone, or were connected from another external network, your device may lose connection and this progress indicator won\'t work. Please wait a while (~ 15 seconds), then check if the connection succeeded.', - 'wifi.conn.explain_reset' => ' + 'wificonn.explain_reset' => ' To force enable the built-in AP, hold the BOOT button until the blue LED starts flashing. Hold the button longer (until the LED flashes rapidly) for a "factory reset".', - 'wifi.conn.disabled' =>"Station mode is disabled.", - 'wifi.conn.idle' =>"Idle, not connected and has no IP.", - 'wifi.conn.success' => "Connected! Received IP ", - 'wifi.conn.working' => "Connecting to selected AP", - 'wifi.conn.fail' => "Connection failed, check settings & try again. Cause: ", + 'wificonn.disabled' =>"Station mode is disabled.", + 'wificonn.idle' =>"Idle, not connected and has no IP.", + 'wificonn.success' => "Connected! Received IP ", + 'wificonn.working' => "Connecting to selected AP", + 'wificonn.fail' => "Connection failed, check settings & try again. Cause: ", - 'system.save_restore' => 'Save & Restore', - 'system.confirm_restore' => 'Restore all settings to their default values?', - 'system.confirm_restore_hard' => - 'Restore to firmware default settings? This will reset ' . - 'all active settings and switch to AP mode with the default SSID.', - 'system.confirm_store_defaults' => - 'Enter admin password to confirm you want to overwrite the default settings.', - 'system.password' => 'Admin password:', - 'system.restore_defaults' => 'Reset to saved defaults', - 'system.write_defaults' => 'Save active settings as defaults', - 'system.restore_hard' => 'Reset active settings to firmware defaults', - 'system.explain_persist' => ' - ESPTerm saves all settings in Flash. The active settings can be copied to - the "defaults area" and restored later using the blue button below. - ', - 'system.uart' => 'Serial Port', - 'system.explain_uart' => ' - This form controls the primary, communication UART. The debug UART is fixed - at 115.200 baud, one stop-bit and no parity. - ', + // Access restrictions form - 'system.security' => 'Access Restrictions', - 'system.explain_security' => ' + 'pwlock.title' => 'Access Restrictions', + 'pwlock.explain' => ' Some parts, or all of the web interface can be protected by a password prompt. - Leave the new password fields empty if you do not wish to change it. + Leave the new password fields empty if you do not wish to change it.
+ The default password is "%def_access_pw%". ', - 'system.pwlock' => 'Protected pages', - 'system.pwlock.none' => 'None, all open', - 'system.pwlock.settings_noterm' => 'WiFi, Net & System settings', - 'system.pwlock.settings' => 'All settings pages', - 'system.pwlock.menus' => 'This entire menu section', - 'system.pwlock.all' => 'Everything, even terminal', - 'system.new_access_pw' => 'New password', - 'system.new_access_pw2' => 'New pass., repeat', - 'system.admin_pw' => 'Admin password', - 'system.access_name' => 'Username', - - 'system.change_adminpw' => 'Change Admin Password', - 'system.explain_adminpw' => + 'pwlock.region' => 'Protected pages', + 'pwlock.region.none' => 'None, all open', + 'pwlock.region.settings_noterm' => 'WiFi, Net & System settings', + 'pwlock.region.settings' => 'All settings pages', + 'pwlock.region.menus' => 'This entire menu section', + 'pwlock.region.all' => 'Everything, even terminal', + 'pwlock.new_access_pw' => 'New password', + 'pwlock.new_access_pw2' => 'Repeat', + 'pwlock.admin_pw' => 'Admin password', + 'pwlock.access_name' => 'Username', + + // Setting admin password + + 'adminpw.title' => 'Change Admin Password', + 'adminpw.explain' => ' The "admin password" is used to manipulate the stored default settings and to change access restrictions. This password is not saved as part of the main config, i.e. using save / restore does not affect this password. When the admin password is forgotten, the easiest way to - re-gain access is to wipe and re-flash the chip. + re-gain access is to wipe and re-flash the chip.
+ The default admin password is "%def_admin_pw%". + ', + 'adminpw.new_admin_pw' => 'New admin password', + 'adminpw.new_admin_pw2' => 'Repeat', + 'adminpw.old_admin_pw' => 'Old admin password', + + // Persist form + + 'persist.title' => 'Save & Restore', + 'persist.explain' => ' + ESPTerm saves all settings in Flash. The active settings can be copied to + the "defaults area" and restored later using the blue button below. ', - 'system.new_admin_pw' => 'New admin pass.', - 'system.new_admin_pw2' => 'New pass., repeat', - 'system.old_admin_pw' => 'Old admin pass.', + 'persist.confirm_restore' => 'Restore all settings to their default values?', + 'persist.confirm_restore_hard' => + 'Restore to firmware default settings? This will reset ' . + 'all active settings and switch to AP mode with the default SSID.', + 'persist.confirm_store_defaults' => + 'Enter admin password to confirm you want to overwrite the default settings.', + 'persist.password' => 'Admin password:', + 'persist.restore_defaults' => 'Reset to saved defaults', + 'persist.write_defaults' => 'Save active settings as defaults', + 'persist.restore_hard' => 'Reset active settings to factory defaults', + 'persist.restore_hard_explain' => '(This clears the WiFi config! Does not affect saved defaults or admin password.)', + // UART settings form + + 'uart.title' => 'Serial Port Parameters', + 'uart.explain' => ' + This form controls the communication UART. The debug UART is fixed + at 115.200 baud, one stop-bit and no parity. + ', 'uart.baud' => 'Baud rate', 'uart.parity' => 'Parity', 'uart.parity.none' => 'None', @@ -223,6 +238,19 @@ return [ 'uart.stop_bits.one_and_half' => 'One and half', 'uart.stop_bits.two' => 'Two', + // HW tuning form + + 'hwtuning.title' => 'Hardware Tuning', + 'hwtuning.explain' => ' + ESP8266 can be overclocked from 80 MHz to 160 MHz. + This will make it more responsive and allow faster screen updates + at the expense of slightly higher power consumption. This can also make + it more susceptible to interference. Use with care. + ', + 'hwtuning.overclock' => 'Overclock to 160MHz', + + // Generic button / dialog labels + 'apply' => 'Apply!', 'enabled' => 'Enabled', 'disabled' => 'Disabled', diff --git a/pages/_head.php b/pages/_head.php index 9466a12..af974b9 100644 --- a/pages/_head.php +++ b/pages/_head.php @@ -32,4 +32,6 @@ if (strpos($_GET['BODYCLASS'], 'cfg') !== false) {   + + diff --git a/pages/cfg_network.php b/pages/cfg_network.php index d9b602d..7116b04 100644 --- a/pages/cfg_network.php +++ b/pages/cfg_network.php @@ -2,6 +2,7 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"'; ?> +

@@ -35,6 +36,7 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
+

@@ -73,6 +75,7 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
+

diff --git a/pages/cfg_system.php b/pages/cfg_system.php index bcf99f9..edae7c7 100644 --- a/pages/cfg_system.php +++ b/pages/cfg_system.php @@ -1,107 +1,131 @@ +
-

+

- +
-
+ -
- +
+
-
- + - - + +
+
+ +
+

+ +
+ +
+ +
+ + +
+ +
+ +
+
+ + -
-

+ + +

- +
- +
- +
- + >
- + >
- +
- +
-
-

+ + +

- +
- +
- +
- +
- +