Merge pull request #30 from MightyPork/wifi-redesign
Settings overhaul & exposed many new wifi settingspull/111/merge
commit
9a9ed1445a
@ -1 +1 @@ |
|||||||
_test_env.php |
pages/_test_env.php |
||||||
|
@ -0,0 +1,62 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Those replacements are done by the development server to test it locally |
||||||
|
* without esphttpd. This is needed mainly for places where the replacements |
||||||
|
* are given to JavaScript, to avoid syntax errors with %% |
||||||
|
*/ |
||||||
|
return [ |
||||||
|
'%term_title%' => 'ESP8266 Wireless Terminal', |
||||||
|
|
||||||
|
'%btn1%' => '1', |
||||||
|
'%btn2%' => '2', |
||||||
|
'%btn3%' => '3', |
||||||
|
'%btn4%' => '4', |
||||||
|
'%btn5%' => '5', |
||||||
|
|
||||||
|
'%screenData%' => '{ |
||||||
|
"w": 26, "h": 10, |
||||||
|
"x": 0, "y": 0, |
||||||
|
"cv": 1, |
||||||
|
"screen": "70 t259" |
||||||
|
}', |
||||||
|
|
||||||
|
'%ap_enable%' => '1', |
||||||
|
'%tpw%' => '60', |
||||||
|
'%ap_channel%' => '7', |
||||||
|
'%ap_ssid%' => 'ESP-123456', |
||||||
|
'%ap_password%' => 'Passw0rd!', |
||||||
|
'%ap_hidden%' => '0', |
||||||
|
'%sta_ssid%' => 'Chlivek', |
||||||
|
'%sta_password%' => 'windows XP is The Best', |
||||||
|
'%sta_active_ip%' => '1.2.3.4', |
||||||
|
'%sta_active_ssid%' => 'Chlivek', |
||||||
|
|
||||||
|
'%sta_enable%' => '1', |
||||||
|
'%opmode%' => '3', |
||||||
|
'%vers_fw%' => '1.2.3', |
||||||
|
'%date%' => date('Y-m-d'), |
||||||
|
'%time%' => date('G:i'), |
||||||
|
'%vers_httpd%' => '4.5.6', |
||||||
|
'%vers_sdk%' => '1.52', |
||||||
|
'%githubrepo%' => 'https://github.com/MightyPork/esp-vt100-firmware', |
||||||
|
|
||||||
|
'%ap_dhcp_time%' => '120', |
||||||
|
'%ap_dhcp_start%' => '192.168.4.100', |
||||||
|
'%ap_dhcp_end%' => '192.168.4.200', |
||||||
|
'%ap_addr_ip%' => '192.168.4.1', |
||||||
|
'%ap_addr_mask%' => '255.255.255.0', |
||||||
|
|
||||||
|
'%sta_dhcp_enable%' => '1', |
||||||
|
'%sta_addr_ip%' => '192.168.0.33', |
||||||
|
'%sta_addr_mask%' => '255.255.255.0', |
||||||
|
'%sta_addr_gw%' => '192.168.0.1', |
||||||
|
|
||||||
|
'%sta_mac%' => 'ab:cd:ef:01:23:45', |
||||||
|
'%ap_mac%' => '01:23:45:ab:cd:ef', |
||||||
|
|
||||||
|
'%term_width%' => '26', |
||||||
|
'%term_height%' => '10', |
||||||
|
'%default_bg%' => '0', |
||||||
|
'%default_fg%' => '7', |
||||||
|
]; |
@ -0,0 +1,3 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
define("ESP_IP", "192.168.0.19"); |
@ -0,0 +1,44 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
$pages = []; |
||||||
|
|
||||||
|
if (! function_exists('pg')) { |
||||||
|
/** Add a page */ |
||||||
|
function pg($key, $bc, $icon, $path, $titleKey = null) |
||||||
|
{ |
||||||
|
global $pages; |
||||||
|
$pages[$key] = (object) [ |
||||||
|
'key' => $key, |
||||||
|
'bodyclass' => $bc, |
||||||
|
'path' => $path, |
||||||
|
'icon' => $icon ? "icn-$icon" : '', |
||||||
|
'label' => tr("menu.$key"), |
||||||
|
'title' => $titleKey ? tr($titleKey) : tr("menu.$key"), |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pg('cfg_wifi', 'cfg', 'wifi', '/cfg/wifi'); |
||||||
|
pg('cfg_wifi_conn', '', '', '/cfg/wifi/connecting'); |
||||||
|
pg('wifi_connstatus', 'api', '', '/cfg/wifi/connstatus'); |
||||||
|
pg('wifi_set', 'api', '', '/cfg/wifi/set'); |
||||||
|
pg('wifi_scan', 'api', '', '/cfg/wifi/scan'); |
||||||
|
|
||||||
|
pg('cfg_network', 'cfg', 'network', '/cfg/network'); |
||||||
|
pg('network_set', 'api', '', '/cfg/network/set'); |
||||||
|
|
||||||
|
pg('cfg_app', 'cfg', 'terminal', '/cfg/app'); |
||||||
|
pg('app_set', 'api', '', '/cfg/app/set'); |
||||||
|
|
||||||
|
pg('cfg_admin', 'cfg', 'persist', '/cfg/admin'); |
||||||
|
pg('write_defaults', 'api', '', '/cfg/admin/write_defaults'); |
||||||
|
pg('restore_defaults', 'api', '', '/cfg/admin/restore_defaults'); |
||||||
|
pg('restore_hard', 'api', '', '/cfg/admin/restore_hard'); |
||||||
|
|
||||||
|
pg('help', 'cfg page-help', 'help', '/help'); |
||||||
|
pg('about', 'cfg page-about', 'about', '/about'); |
||||||
|
pg('term', 'term', '', '/', 'title.term'); |
||||||
|
|
||||||
|
// ajax API |
||||||
|
|
||||||
|
return $pages; |
@ -1,80 +0,0 @@ |
|||||||
<!DOCTYPE html> |
|
||||||
<html> |
|
||||||
<head> |
|
||||||
<meta charset="UTF-8"> |
|
||||||
<title>About - ESP8266 Remote Terminal</title> |
|
||||||
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"> |
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/app.css"> |
|
||||||
<script src="/js/app.js"></script> |
|
||||||
</head> |
|
||||||
<body class="page-about"> |
|
||||||
|
|
||||||
<h1 onclick="location.href='/'">About</h1> |
|
||||||
|
|
||||||
<div class="Box"> |
|
||||||
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min"> |
|
||||||
<h2>ESP8266 Remote Terminal</h2> |
|
||||||
|
|
||||||
<img src="/img/cvut.svg" id="logo2" class="mq-phone"> |
|
||||||
|
|
||||||
<p>© Ondřej Hruška, 2017 <<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>></p> |
|
||||||
|
|
||||||
<p><a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>Department of Measurement, FEE CTU</p> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="Box"> |
|
||||||
<h2>Firmware</h2> |
|
||||||
<table> |
|
||||||
<tr> |
|
||||||
<th>Firmware</th> |
|
||||||
<td>v%vers_fw%, build <i>%date%</i> at <i>%time%</i></td> |
|
||||||
</tr> |
|
||||||
<tr> |
|
||||||
<th>libesphttpd</th> |
|
||||||
<td>v%vers_httpd%</td> |
|
||||||
</tr> |
|
||||||
<tr> |
|
||||||
<th>ESP IoT SDK</th> |
|
||||||
<td>v%vers_sdk%</td> |
|
||||||
</tr> |
|
||||||
</table> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="Box"> |
|
||||||
<h2>Issues</h2> |
|
||||||
<p> |
|
||||||
Please report any issues to the <a href="%githubrepo%/issues">bugtracker</a> or send them by e-mail. |
|
||||||
</p> |
|
||||||
<p> |
|
||||||
Firmware updates can be downloaded from the <a href="%githubrepo%/releases">releases page</a>. |
|
||||||
Flash the images using <a href="https://github.com/espressif/esptool">esptool</a>. |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="Box"> |
|
||||||
<h2>Contributing</h2> |
|
||||||
<p> |
|
||||||
Submit your improvements and ideas to the project on <a href="%githubrepo%">GitHub</a>.<br> |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="Box"> |
|
||||||
<h2>Acknowledgements</h2> |
|
||||||
<p> |
|
||||||
The webserver is based on a <a href="https://github.com/MightyPork/libesphttpd">fork</a> of the |
|
||||||
<a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm). |
|
||||||
</p> |
|
||||||
<p> |
|
||||||
Using (modified) JS library <a href="https://github.com/kylebarrow/chibi">chibi.js</a> by Kyle Barrow as a lightweight jQuery alternative. |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
|
|
||||||
<nav id="botnav"> |
|
||||||
<a href="/">Terminal</a><!-- |
|
||||||
--><a href="/help">Help</a><!-- |
|
||||||
--><a href="/wifi">WiFi config</a> |
|
||||||
</nav> |
|
||||||
|
|
||||||
</body> |
|
||||||
</html> |
|
@ -0,0 +1,71 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* The common stuff required by both index.php and build_html.php |
||||||
|
* this must be required_once |
||||||
|
*/ |
||||||
|
|
||||||
|
if (defined('BASE_INITED')) return; |
||||||
|
define('BASE_INITED', true); |
||||||
|
|
||||||
|
if (!empty($argv[1])) { |
||||||
|
parse_str($argv[1], $_GET); |
||||||
|
} |
||||||
|
|
||||||
|
if (!file_exists(__DIR__ . '/_env.php')) { |
||||||
|
die("Copy <b>_env.php.example</b> to <b>_env.php</b> and check the settings inside!"); |
||||||
|
} |
||||||
|
|
||||||
|
require_once __DIR__ . '/_env.php'; |
||||||
|
|
||||||
|
$prod = defined('STDIN'); |
||||||
|
define('DEBUG', !$prod); |
||||||
|
$root = DEBUG ? json_encode(ESP_IP) : 'location.host'; |
||||||
|
define('JS_WEB_ROOT', $root); |
||||||
|
|
||||||
|
define('LOCALE', isset($_GET['locale']) ? $_GET['locale'] : 'en'); |
||||||
|
|
||||||
|
$_messages = require(__DIR__ . '/lang/' . LOCALE . '.php'); |
||||||
|
$_pages = require(__DIR__ . '/_pages.php'); |
||||||
|
|
||||||
|
define('APP_NAME', 'ESPTerm'); |
||||||
|
|
||||||
|
/** URL (dev or production) */ |
||||||
|
function url($name, $relative = false) |
||||||
|
{ |
||||||
|
global $_pages; |
||||||
|
if ($relative) return $_pages[$name]->path; |
||||||
|
|
||||||
|
if (DEBUG) return "/index.php?page=$name"; |
||||||
|
else return $_pages[$name]->path; |
||||||
|
} |
||||||
|
|
||||||
|
/** URL label for a button */ |
||||||
|
function label($name) |
||||||
|
{ |
||||||
|
global $_pages; |
||||||
|
return $_pages[$name]->label; |
||||||
|
} |
||||||
|
|
||||||
|
function e($s) |
||||||
|
{ |
||||||
|
return htmlspecialchars($s, ENT_HTML5 | ENT_QUOTES); |
||||||
|
} |
||||||
|
|
||||||
|
function tr($key) |
||||||
|
{ |
||||||
|
global $_messages; |
||||||
|
return isset($_messages[$key]) ? $_messages[$key] : ('??' . $key . '??'); |
||||||
|
} |
||||||
|
|
||||||
|
/** Like eval, but allows <?php and ?> */
|
||||||
|
function include_str($code) |
||||||
|
{ |
||||||
|
$tmp = tmpfile(); |
||||||
|
$tmpf = stream_get_meta_data($tmp); |
||||||
|
$tmpf = $tmpf ['uri']; |
||||||
|
fwrite($tmp, $code); |
||||||
|
$ret = include($tmpf); |
||||||
|
fclose($tmp); |
||||||
|
return $ret; |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php'; |
||||||
|
|
||||||
|
function process_html($s) { |
||||||
|
$pattern = '/<!--(.*)-->/Uis'; |
||||||
|
$s = preg_replace($pattern, '', $s); |
||||||
|
|
||||||
|
$pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\'|\")\/\/.*))/'; |
||||||
|
$s = preg_replace($pattern, '', $s); |
||||||
|
|
||||||
|
$pattern = '/\s+/s'; |
||||||
|
$s = preg_replace($pattern, ' ', $s); |
||||||
|
return $s; |
||||||
|
} |
||||||
|
|
||||||
|
ob_start(); |
||||||
|
foreach($_pages as $_k => $p) { |
||||||
|
if ($p->bodyclass == 'api') continue; |
||||||
|
echo "Generating: $_k ($p->title)\n"; |
||||||
|
$_GET['page'] = $_k; |
||||||
|
ob_flush(); // print the message |
||||||
|
ob_clean(); // clean up |
||||||
|
include(__DIR__ . '/index.php'); |
||||||
|
$s = ob_get_contents(); // grab the output |
||||||
|
|
||||||
|
// remove newlines and comments |
||||||
|
// as tests have shown, it saves just a couple kilobytes, |
||||||
|
// making it not a very big improvement at the expense of ugly html. |
||||||
|
// $s = process_html($s); |
||||||
|
|
||||||
|
ob_clean(); // clean up |
||||||
|
$of = __DIR__ . '/../html/' . $_k . '.tpl'; |
||||||
|
file_put_contents($of, $s); // write to a file |
||||||
|
} |
||||||
|
|
||||||
|
ob_flush(); |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,21 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/* This script is run on demand to generate JS version of tr() */ |
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php'; |
||||||
|
|
||||||
|
$selected = [ |
||||||
|
'wifi.connected_ip_is', |
||||||
|
'wifi.not_conn', |
||||||
|
]; |
||||||
|
|
||||||
|
$out = []; |
||||||
|
foreach ($selected as $key) { |
||||||
|
$out[$key] = $_messages[$key]; |
||||||
|
} |
||||||
|
|
||||||
|
file_put_contents(__DIR__. '/jssrc/lang.js', |
||||||
|
"// Generated from PHP locale file\n" . |
||||||
|
'var _tr = ' . json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) . ";\n\n" . |
||||||
|
"function tr(key) { return _tr[key] || '?'+key+'?'; }\n" |
||||||
|
); |
Binary file not shown.
@ -0,0 +1,21 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php'; |
||||||
|
|
||||||
|
if (!isset($_GET['page'])) $_GET['page'] = 'term'; |
||||||
|
|
||||||
|
$_GET['PAGE_TITLE'] = $_pages[$_GET['page']]->title . ' :: ' . APP_NAME; |
||||||
|
$_GET['BODYCLASS'] = $_pages[$_GET['page']]->bodyclass; |
||||||
|
|
||||||
|
require __DIR__ . '/pages/_head.php'; |
||||||
|
$_pf = __DIR__ . '/pages/'.$_GET['page'].'.php'; |
||||||
|
if (file_exists($_pf)) { |
||||||
|
$f = file_get_contents($_pf); |
||||||
|
$reps = DEBUG ? require(__DIR__ . '/_debug_replacements.php') : []; |
||||||
|
$str = str_replace(array_keys($reps), array_values($reps), $f); |
||||||
|
include_str($str); |
||||||
|
} else { |
||||||
|
echo "404"; |
||||||
|
} |
||||||
|
|
||||||
|
require __DIR__ . '/pages/_tail.php'; |
@ -0,0 +1,7 @@ |
|||||||
|
// Generated from PHP locale file
|
||||||
|
var _tr = { |
||||||
|
"wifi.connected_ip_is": "Connected, IP is ", |
||||||
|
"wifi.not_conn": "Not connected." |
||||||
|
}; |
||||||
|
|
||||||
|
function tr(key) { return _tr[key] || '?'+key+'?'; } |
@ -0,0 +1,32 @@ |
|||||||
|
(function (nt) { |
||||||
|
var sel = '#notif'; |
||||||
|
|
||||||
|
var hideTmeo1; // timeout to start hiding (transition)
|
||||||
|
var hideTmeo2; // timeout to add the hidden class
|
||||||
|
|
||||||
|
nt.show = function (message, timeout) { |
||||||
|
$(sel).html(message); |
||||||
|
Modal.show(sel); |
||||||
|
|
||||||
|
clearTimeout(hideTmeo1); |
||||||
|
clearTimeout(hideTmeo2); |
||||||
|
|
||||||
|
if (undef(timeout)) timeout = 2500; |
||||||
|
|
||||||
|
hideTmeo1 = setTimeout(nt.hide, timeout); |
||||||
|
}; |
||||||
|
|
||||||
|
nt.hide = function () { |
||||||
|
var $m = $(sel); |
||||||
|
$m.removeClass('visible'); |
||||||
|
hideTmeo2 = setTimeout(function () { |
||||||
|
$m.addClass('hidden'); |
||||||
|
}, 250); // transition time
|
||||||
|
}; |
||||||
|
|
||||||
|
nt.init = function() { |
||||||
|
$(sel).on('click', function() { |
||||||
|
nt.hide(this); |
||||||
|
}); |
||||||
|
}; |
||||||
|
})(window.Notify = {}); |
@ -0,0 +1,140 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return [ |
||||||
|
'appname' => 'ESPTerm', |
||||||
|
|
||||||
|
'menu.cfg_wifi' => 'WiFi Settings', |
||||||
|
'menu.cfg_network' => 'Network Configuration', |
||||||
|
'menu.cfg_app' => 'Terminal Settings', |
||||||
|
'menu.about' => 'About ESPTerm', |
||||||
|
'menu.help' => 'Quick Reference', |
||||||
|
'menu.term' => 'Back to Terminal', |
||||||
|
'menu.cfg_admin' => 'Reset & Restore', |
||||||
|
'menu.cfg_wifi_conn' => 'Connecting to External Network', |
||||||
|
|
||||||
|
'menu.settings' => 'Settings', |
||||||
|
|
||||||
|
'title.term' => 'Terminal', |
||||||
|
|
||||||
|
'net.ap' => 'DHCP Server (AP)', |
||||||
|
'net.sta' => 'DHCP Client (Station)', |
||||||
|
'net.sta_mac' => 'Station MAC', |
||||||
|
'net.ap_mac' => 'AP MAC', |
||||||
|
'net.details' => 'MAC addresses', |
||||||
|
|
||||||
|
'app.defaults' => 'Initial settings', |
||||||
|
'app.explain_initials' => ' |
||||||
|
Those are the initial settings used after ESPTerm restarts, and they |
||||||
|
will also be applied immediately after you submit this form. |
||||||
|
They can be subsequently changed by ESC commands, but those changes |
||||||
|
aren\'t persistent and will be lost when the device powers off.', |
||||||
|
|
||||||
|
'app.term_title' => 'Header text', |
||||||
|
'app.term_width' => 'Screen width', |
||||||
|
'app.term_height' => 'Screen height', |
||||||
|
'app.default_fg' => 'Base text color', |
||||||
|
'app.default_bg' => 'Base background', |
||||||
|
'app.btn1' => 'Button 1 text', |
||||||
|
'app.btn2' => 'Button 2 text', |
||||||
|
'app.btn3' => 'Button 3 text', |
||||||
|
'app.btn4' => 'Button 4 text', |
||||||
|
'app.btn5' => 'Button 5 text', |
||||||
|
|
||||||
|
'color.0' => 'Black', |
||||||
|
'color.1' => 'Dark Red', |
||||||
|
'color.2' => 'Dark Green', |
||||||
|
'color.3' => 'Dim Yellow', |
||||||
|
'color.4' => 'Deep Blue', |
||||||
|
'color.5' => 'Dark Violet', |
||||||
|
'color.6' => 'Dark 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 Violet', |
||||||
|
'color.14' => 'Light Cyan', |
||||||
|
'color.15' => 'White', |
||||||
|
|
||||||
|
'net.explain_sta' => ' |
||||||
|
Those settings affect the built-in DHCP client used for |
||||||
|
connecting to an external network. Switching DHCP (dynamic IP) off |
||||||
|
makes ESPTerm use the configured static IP. Please double-check |
||||||
|
those settings before submitting, setting them incorrectly may |
||||||
|
make it hard to access ESPTerm via the external network.', |
||||||
|
|
||||||
|
'net.explain_ap' => ' |
||||||
|
Those settings affect the built-in DHCP server in AP mode. |
||||||
|
Please double-check those settings before submitting, setting them |
||||||
|
incorrectly may render ESPTerm inaccessible via the AP.', |
||||||
|
|
||||||
|
'net.ap_dhcp_time' => 'Lease time', |
||||||
|
'net.ap_dhcp_start' => 'Pool start IP', |
||||||
|
'net.ap_dhcp_end' => 'Pool end IP', |
||||||
|
'net.ap_addr_ip' => 'Own IP address', |
||||||
|
'net.ap_addr_mask' => 'Subnet mask', |
||||||
|
|
||||||
|
'net.sta_dhcp_enable' => 'Use dynamic IP', |
||||||
|
'net.sta_addr_ip' => 'ESPTerm static IP', |
||||||
|
'net.sta_addr_mask' => 'Subnet mask', |
||||||
|
'net.sta_addr_gw' => 'Gateway IP', |
||||||
|
|
||||||
|
'wifi.ap' => 'Built-in Access Point', |
||||||
|
'wifi.sta' => 'Connect to External Network', |
||||||
|
|
||||||
|
'wifi.enable' => 'Enabled', |
||||||
|
'wifi.tpw' => 'Transmit power', |
||||||
|
'wifi.ap_channel' => 'Channel', |
||||||
|
'wifi.ap_ssid' => 'AP SSID', |
||||||
|
'wifi.ap_password' => 'Password', |
||||||
|
'wifi.ap_hidden' => 'Hide SSID', |
||||||
|
'wifi.sta_info' => 'Selected', |
||||||
|
|
||||||
|
'wifi.not_conn' => 'Not connected.', |
||||||
|
'wifi.sta_none' => 'None', |
||||||
|
'wifi.sta_active_pw' => '🔒', |
||||||
|
'wifi.sta_active_nopw' => '🔓 Open access', |
||||||
|
'wifi.connected_ip_is' => 'Connected, IP is ', |
||||||
|
'wifi.sta_password' => 'Password:', |
||||||
|
|
||||||
|
'wifi.scanning' => 'Scanning', |
||||||
|
'wifi.scan_now' => 'Start scanning!', |
||||||
|
'wifi.cant_scan_no_sta' => 'Can\'t scan with Client mode disabled.', |
||||||
|
'wifi.select_ssid' => 'Available networks:', |
||||||
|
|
||||||
|
'wifi.conn.status' => 'Status:', |
||||||
|
'wifi.conn.back_to_config' => 'Back to WiFi config', |
||||||
|
'wifi.conn.telemetry_lost' => 'Telemetry lost, something went wrong. Try again...', |
||||||
|
|
||||||
|
'wifi.conn.disabled' =>"Station mode is disabled.", |
||||||
|
'wifi.conn.idle' =>"Idle, not connected and with 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: ", |
||||||
|
|
||||||
|
'admin.confirm_restore' => 'Restore all settings to their default values?', |
||||||
|
'admin.confirm_restore_hard' => |
||||||
|
'Restore to firmware default settings? This will reset ' . |
||||||
|
'all active settings and switch to AP mode with the default SSID.', |
||||||
|
'admin.confirm_store_defaults' => |
||||||
|
'Enter admin password to confirm you want to store the current settings as defaults.', |
||||||
|
'admin.password' => 'Admin password:', |
||||||
|
'admin.restore_defaults' => 'Reset to default settings', |
||||||
|
'admin.write_defaults' => 'Save current settings as default', |
||||||
|
'admin.restore_hard' => 'Reset to firmware default settings', |
||||||
|
'admin.explain' => ' |
||||||
|
ESPTerm contains two persistent memory banks, one for default and |
||||||
|
one for active settings. Active settings can be stored as defaults |
||||||
|
by the administrator. Use the following button to revert all |
||||||
|
active settings to their stored default values. |
||||||
|
', |
||||||
|
|
||||||
|
'apply' => 'Apply!', |
||||||
|
'enabled' => 'Enabled', |
||||||
|
'disabled' => 'Disabled', |
||||||
|
'yes' => 'Yes', |
||||||
|
'no' => 'No', |
||||||
|
'confirm' => 'OK', |
||||||
|
'form_errors' => 'Validation errors for:', |
||||||
|
]; |
@ -0,0 +1,20 @@ |
|||||||
|
<nav id="menu"> |
||||||
|
<div id="brand" tabindex=0><?= tr('appname') ?></div>
|
||||||
|
<a href="<?= e(url('term')) ?>" class="icn-back"><?= tr('menu.term') ?></a>
|
||||||
|
<?php |
||||||
|
// generate the menu |
||||||
|
foreach ($_pages as $k => $page) { |
||||||
|
if (strpos($page->bodyclass, 'cfg') === false) continue; |
||||||
|
|
||||||
|
$sel = ($_GET['page'] == $k) ? 'selected' : ''; |
||||||
|
$text = $page->label; |
||||||
|
$url = e(url($k)); |
||||||
|
echo "<a href=\"$url\" class=\"$page->icon $sel\">$text</a>"; |
||||||
|
} |
||||||
|
?> |
||||||
|
</nav> |
||||||
|
|
||||||
|
<script> |
||||||
|
function menuOpen() { $('#menu').toggleClass('expanded') } |
||||||
|
$('#brand').on('click', menuOpen).on('keypress', cr(menuOpen)); |
||||||
|
</script> |
@ -0,0 +1,31 @@ |
|||||||
|
<!doctype html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||||
|
<title><?= $_GET['PAGE_TITLE'] ?></title>
|
||||||
|
<link href="/css/app.css" rel="stylesheet"> |
||||||
|
<script src="/js/app.js"></script> |
||||||
|
<script>var _root = <?= JS_WEB_ROOT ?>;</script>
|
||||||
|
</head> |
||||||
|
<body class="<?= $_GET['BODYCLASS'] ?>">
|
||||||
|
<div id="outer"> |
||||||
|
<?php |
||||||
|
$cfg = false; |
||||||
|
if (strpos($_GET['BODYCLASS'], 'cfg') !== false) { |
||||||
|
$cfg = true; |
||||||
|
require __DIR__ . '/_cfg_menu.php'; |
||||||
|
} |
||||||
|
?> |
||||||
|
|
||||||
|
<div id="content"> |
||||||
|
<img src="/img/loader.gif" alt="Loading…" id="loader"> |
||||||
|
<?php if ($cfg): ?> |
||||||
|
<h1><?= tr('menu.' . $_GET['page']) ?></h1>
|
||||||
|
|
||||||
|
<div class="Box errors hidden"> |
||||||
|
<span class="lead"><?= tr('form_errors') ?></span> <span class="list"></span>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<?php endif; ?> |
@ -0,0 +1,9 @@ |
|||||||
|
|
||||||
|
<div class="NotifyMsg hidden" id="notif"></div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,69 @@ |
|||||||
|
<div class="Box"> |
||||||
|
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min"> |
||||||
|
<h2>ESP8266 Remote Terminal</h2> |
||||||
|
|
||||||
|
<img src="/img/cvut.svg" id="logo2" class="mq-phone"> |
||||||
|
|
||||||
|
<p> |
||||||
|
© Ondřej Hruška, 2016-2017 |
||||||
|
<<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>> |
||||||
|
</p> |
||||||
|
|
||||||
|
<p> |
||||||
|
<a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br> |
||||||
|
Department of Measurement, FEE CTU |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Box"> |
||||||
|
<h2>Version</h2> |
||||||
|
<table> |
||||||
|
<tr> |
||||||
|
<th>ESPTerm</th> |
||||||
|
<td>v%vers_fw%, build %date% at %time%</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th>libesphttpd</th> |
||||||
|
<td>v%vers_httpd%</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th>ESP IoT SDK</th> |
||||||
|
<td>v%vers_sdk%</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Box"> |
||||||
|
<h2>Issues</h2> |
||||||
|
<p> |
||||||
|
Please report any issues to the <a href="%githubrepo%/issues">bugtracker</a> or send them by e-mail (see above). |
||||||
|
</p> |
||||||
|
<p> |
||||||
|
Firmware updates can be downloaded from the <a href="%githubrepo%/releases">releases page</a> and flashed |
||||||
|
with <a href="https://github.com/espressif/esptool">esptool.py</a>. |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Box"> |
||||||
|
<h2>Contributing</h2> |
||||||
|
<p> |
||||||
|
<i class="icn-github"></i> You're welcome to submit your improvements and ideas to our <a href="%githubrepo%">GitHub repository</a>! |
||||||
|
</p> |
||||||
|
|
||||||
|
<p> |
||||||
|
<i class="icn-donate"></i> If you'd like to donate, please try <a href="https://paypal.me/mightypork">PayPal</a> or |
||||||
|
<a href="https://liberapay.com/MightyPork/">LiberaPay</a>. |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Box"> |
||||||
|
<h2>Thanks</h2> |
||||||
|
<p> |
||||||
|
The webserver is based on a <a href="https://github.com/MightyPork/libesphttpd">fork</a> of the |
||||||
|
<a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm). |
||||||
|
</p> |
||||||
|
<p> |
||||||
|
Using (modified) JS library <a href="https://github.com/kylebarrow/chibi">chibi.js</a> by |
||||||
|
Kyle Barrow as a lightweight jQuery alternative. |
||||||
|
</p> |
||||||
|
</div> |
@ -0,0 +1,34 @@ |
|||||||
|
<div class="Box"> |
||||||
|
<div class="Row explain"> |
||||||
|
<?= tr('admin.explain') ?> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a class="button icn-restore" |
||||||
|
onclick="return confirm('<?= tr('admin.confirm_restore') ?>');"
|
||||||
|
href="<?= e(url('restore_defaults')) ?>"
|
||||||
|
> |
||||||
|
<?= tr('admin.restore_defaults') ?> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a onclick="writeDefaults(); return false;" href="#"><?= tr('admin.write_defaults') ?></a>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a onclick="return confirm('<?= tr('admin.confirm_restore_hard') ?>');"
|
||||||
|
href="<?= e(url('restore_hard')) ?>"
|
||||||
|
> |
||||||
|
<?= tr('admin.restore_hard') ?> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
function writeDefaults() { |
||||||
|
var pw = prompt('<?= tr('admin.confirm_store_defaults') ?>');
|
||||||
|
if (!pw) return; |
||||||
|
location.href = <?=json_encode(url('write_defaults')) ?> + '?pw=' + pw;
|
||||||
|
} |
||||||
|
</script> |
@ -0,0 +1,71 @@ |
|||||||
|
<form class="Box mobopen str" action="<?= e(url('app_set')) ?>" method="GET" id='form-1'>
|
||||||
|
<h2><?= tr('app.defaults') ?></h2>
|
||||||
|
|
||||||
|
<div class="Row explain"> |
||||||
|
<?= tr('app.explain_initials') ?> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="term_width"><?= tr('app.term_width') ?></label>
|
||||||
|
<input type="number" step=1 min=1 max=255 name="term_width" id="term_width" value="%term_width%" required> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="term_height"><?= tr('app.term_height') ?></label>
|
||||||
|
<input type="number" step=1 min=1 max=255 name="term_height" id="term_height" value="%term_height%" required> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="default_fg"><?= tr("app.default_fg") ?></label>
|
||||||
|
<select name="default_fg" id="default_fg"> |
||||||
|
<?php for($i=0; $i<16; $i++): ?> |
||||||
|
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
|
||||||
|
<?php endfor; ?> |
||||||
|
</select> |
||||||
|
<script>qs('#default_fg').selectedIndex = %default_fg%;</script> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="default_bg"><?= tr("app.default_bg") ?></label>
|
||||||
|
<select name="default_bg" id="default_bg"> |
||||||
|
<?php for($i=0; $i<16; $i++): ?> |
||||||
|
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
|
||||||
|
<?php endfor; ?> |
||||||
|
</select> |
||||||
|
<script>qs('#default_bg').selectedIndex = %default_bg%;</script> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="term_title"><?= tr('app.term_title') ?></label>
|
||||||
|
<input type="text" name="term_title" id="term_title" value="%term_title%" required> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="btn1"><?= tr("app.btn1") ?></label>
|
||||||
|
<input class="short" type="text" name="btn1" id="btn1" value="%btn1%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="btn2"><?= tr("app.btn2") ?></label>
|
||||||
|
<input class="short" type="text" name="btn2" id="btn2" value="%btn2%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="btn3"><?= tr("app.btn3") ?></label>
|
||||||
|
<input class="short" type="text" name="btn3" id="btn3" value="%btn3%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="btn4"><?= tr("app.btn4") ?></label>
|
||||||
|
<input class="short" type="text" name="btn4" id="btn4" value="%btn4%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="btn5"><?= tr("app.btn5") ?></label>
|
||||||
|
<input class="short" type="text" name="btn5" id="btn5" value="%btn5%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
|
||||||
|
</div> |
||||||
|
</form> |
@ -0,0 +1,95 @@ |
|||||||
|
<?php |
||||||
|
$ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"'; |
||||||
|
?> |
||||||
|
|
||||||
|
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-2">
|
||||||
|
<h2 tabindex=0><?= tr('net.sta') ?></h2>
|
||||||
|
|
||||||
|
<div class="Row explain"> |
||||||
|
<?= tr('net.explain_sta') ?> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row checkbox x-static-toggle" > |
||||||
|
<label><?= tr('net.sta_dhcp_enable') ?></label><!--
|
||||||
|
--><span class="box" tabindex=0 role=checkbox></span> |
||||||
|
<input type="hidden" name="sta_dhcp_enable" value="%sta_dhcp_enable%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row x-static"> |
||||||
|
<label for="sta_addr_ip"><?= tr('net.sta_addr_ip') ?></label>
|
||||||
|
<input type="text" name="sta_addr_ip" id="sta_addr_ip" value="%sta_addr_ip%" <?=$ipmask?> required>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row x-static"> |
||||||
|
<label for="sta_addr_mask"><?= tr('net.sta_addr_mask') ?></label>
|
||||||
|
<input type="text" name="sta_addr_mask" id="sta_addr_mask" value="%sta_addr_mask%" <?=$ipmask?> required>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row x-static"> |
||||||
|
<label for="sta_addr_gw"><?= tr('net.sta_addr_gw') ?></label>
|
||||||
|
<input type="text" name="sta_addr_gw" id="sta_addr_gw" value="%sta_addr_gw%" <?=$ipmask?> required>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a class="button icn-ok" href="#" onclick="qs('#form-2').submit()"><?= tr('apply') ?></a>
|
||||||
|
</div> |
||||||
|
</form> |
||||||
|
|
||||||
|
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-1">
|
||||||
|
<h2 tabindex=0><?= tr('net.ap') ?></h2>
|
||||||
|
|
||||||
|
<div class="Row explain"> |
||||||
|
<?= tr('net.explain_ap') ?> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_dhcp_time"><?= tr('net.ap_dhcp_time') ?><span class="mq-phone"> (min)</span></label>
|
||||||
|
<input type="number" step=1 min=1 max=2880 name="ap_dhcp_time" id="ap_dhcp_time" value="%ap_dhcp_time%" required> |
||||||
|
<span class="mq-no-phone"> (min)</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_dhcp_start"><?= tr('net.ap_dhcp_start') ?></label>
|
||||||
|
<input type="text" name="ap_dhcp_start" id="ap_dhcp_start" value="%ap_dhcp_start%" <?=$ipmask?> required>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_dhcp_end"><?= tr('net.ap_dhcp_end') ?></label>
|
||||||
|
<input type="text" name="ap_dhcp_end" id="ap_dhcp_end" value="%ap_dhcp_end%" <?=$ipmask?> required>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_addr_ip"><?= tr('net.ap_addr_ip') ?></label>
|
||||||
|
<input type="text" name="ap_addr_ip" id="ap_addr_ip" value="%ap_addr_ip%" <?=$ipmask?> required>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_addr_mask"><?= tr('net.ap_addr_mask') ?></label>
|
||||||
|
<input type="text" name="ap_addr_mask" id="ap_addr_mask" value="%ap_addr_mask%" <?=$ipmask?> required>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
|
||||||
|
</div> |
||||||
|
</form> |
||||||
|
|
||||||
|
<div class="Box mobcol"> |
||||||
|
<h2><?= tr('net.details') ?></h2>
|
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label><?= tr('net.sta_mac') ?></label><input type="text" readonly value="%sta_mac%">
|
||||||
|
</div> |
||||||
|
<div class="Row"> |
||||||
|
<label><?= tr('net.ap_mac') ?></label><input type="text" readonly value="%ap_mac%">
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
function updateStaticDisp() { |
||||||
|
$('.x-static').toggleClass('hidden', $('#sta_dhcp_enable').val()); |
||||||
|
} |
||||||
|
$('.x-static-toggle').on('click', function() { |
||||||
|
updateStaticDisp(); |
||||||
|
}); |
||||||
|
updateStaticDisp(); |
||||||
|
</script> |
@ -0,0 +1,108 @@ |
|||||||
|
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-1">
|
||||||
|
<h2 tabindex=0><?= tr('wifi.ap') ?></h2>
|
||||||
|
|
||||||
|
<div class="Row checkbox"> |
||||||
|
<label><?= tr('wifi.enable') ?></label><!--
|
||||||
|
--><span class="box" tabindex=0></span> |
||||||
|
<input type="hidden" name="ap_enable" value="%ap_enable%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_ssid"><?= tr('wifi.ap_ssid') ?></label>
|
||||||
|
<input type="text" name="ap_ssid" id="ap_ssid" value="%ap_ssid%" required> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_password"><?= tr('wifi.ap_password') ?></label>
|
||||||
|
<input type="text" name="ap_password" id="ap_password" value="%ap_password%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row"> |
||||||
|
<label for="ap_channel"><?= tr('wifi.ap_channel') ?></label>
|
||||||
|
<input type="number" name="ap_channel" id="ap_channel" min=1 max=14 value="%ap_channel%" required> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row range"> |
||||||
|
<label for="tpw"> |
||||||
|
<?= tr('wifi.tpw') ?> |
||||||
|
<span class="display x-disp1 mq-phone"></span> |
||||||
|
</label> |
||||||
|
<input type="range" name="tpw" id="tpw" step=1 min=0 max=82 value="%tpw%"> |
||||||
|
<span class="display x-disp2 mq-no-phone"></span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row checkbox"> |
||||||
|
<label><?= tr('wifi.ap_hidden') ?></label><!--
|
||||||
|
--><span class="box" tabindex=0></span> |
||||||
|
<input type="hidden" name="ap_hidden" value="%ap_hidden%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
|
||||||
|
</div> |
||||||
|
</form> |
||||||
|
|
||||||
|
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-2">
|
||||||
|
<h2 tabindex=0><?= tr('wifi.sta') ?></h2>
|
||||||
|
|
||||||
|
<div class="Row checkbox"> |
||||||
|
<label><?= tr('wifi.enable') ?></label><!--
|
||||||
|
--><span class="box" tabindex=0></span> |
||||||
|
<input type="hidden" name="sta_enable" value="%sta_enable%"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<input type="hidden" name="sta_ssid" id="sta_ssid" value=""> |
||||||
|
<input type="hidden" name="sta_password" id="sta_password" value=""> |
||||||
|
|
||||||
|
<div class="Row sta-info"> |
||||||
|
<label><?= tr('wifi.sta_info') ?></label>
|
||||||
|
<div class="AP-preview hidden" id="sta-nw"> |
||||||
|
<div class="wrap"> |
||||||
|
<div class="inner"> |
||||||
|
<div class="essid"></div> |
||||||
|
<div class="passwd"><?= tr('wifi.sta_active_pw') ?> <span class="x-passwd"></span></div>
|
||||||
|
<div class="nopasswd"><?= tr('wifi.sta_active_nopw') ?></div>
|
||||||
|
<div class="ip"></div> |
||||||
|
</div> |
||||||
|
<a class="forget" href="#" id="forget-sta">×</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="AP-preview-nil" id="sta-nw-nil"> |
||||||
|
<?= tr('wifi.sta_none') ?> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div id="ap-box"> |
||||||
|
<label><?= tr('wifi.select_ssid') ?></label>
|
||||||
|
<div id="ap-scan"><a href="#" onclick="WiFi.startScanning(); return false"><?= tr('wifi.scan_now') ?></a></div>
|
||||||
|
<div id="ap-loader" class="hidden"><?= tr('wifi.scanning') ?><span class="anim-dots">.</span></div>
|
||||||
|
<div id="ap-noscan" class="hidden"><?= tr('wifi.cant_scan_no_sta') ?></div>
|
||||||
|
<div id="ap-list" class="hidden"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="Row buttons"> |
||||||
|
<a class="button icn-ok" href="#" onclick="qs('#form-2').submit()"><?= tr('apply') ?></a>
|
||||||
|
</div> |
||||||
|
</form> |
||||||
|
|
||||||
|
<div class="Modal hidden" id="psk-modal"> |
||||||
|
<div class="Dialog"> |
||||||
|
<form id="conn-form" onsubmit="submitPskModal(); return false;"> |
||||||
|
<input type="hidden" id="conn-ssid"><!-- |
||||||
|
--><label for="conn-passwd"><?= tr('wifi.sta_password') ?></label><!--
|
||||||
|
--><input type="text" id="conn-passwd"><!-- |
||||||
|
--><input type="submit" value="<?= tr('confirm') ?>">
|
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
WiFi.scan_url = '<?= url('wifi_scan', true) ?>';
|
||||||
|
WiFi.init({ |
||||||
|
mode: '%opmode%', |
||||||
|
sta_ssid: '%sta_ssid%', |
||||||
|
sta_password: '%sta_password%', |
||||||
|
sta_active_ip: '%sta_active_ip%', |
||||||
|
sta_active_ssid: '%sta_active_ssid%', |
||||||
|
}); |
||||||
|
</script> |
@ -0,0 +1,53 @@ |
|||||||
|
<h1><?= tr('menu.cfg_wifi_conn') ?></h1>
|
||||||
|
|
||||||
|
<div class="Box"> |
||||||
|
<p><b><?= tr('wifi.conn.status') ?></b> <span id="status"></span><span class="anim-dots">.</span></p>
|
||||||
|
<a href="<?= e(url('cfg_wifi')) ?>" id="backbtn" class="button"><?= tr('wifi.conn.back_to_config') ?></a>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
var xhr = new XMLHttpRequest(); |
||||||
|
var abortTmeo; |
||||||
|
|
||||||
|
var messages = <?= json_encode([ |
||||||
|
'disabled' => tr('wifi.conn.disabled'), |
||||||
|
'idle' => tr('wifi.conn.idle'), |
||||||
|
'success' => tr('wifi.conn.success'), |
||||||
|
'working' => tr('wifi.conn.working'), |
||||||
|
'fail' => tr('wifi.conn.fail'), |
||||||
|
]) ?>; |
||||||
|
|
||||||
|
function getStatus() { |
||||||
|
xhr.open("GET", 'http://'+_root+'<?= url('wifi_connstatus', true) ?>');
|
||||||
|
xhr.onreadystatechange = function () { |
||||||
|
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) { |
||||||
|
clearTimeout(abortTmeo); |
||||||
|
var data = JSON.parse(xhr.responseText); |
||||||
|
var done = false; |
||||||
|
var msg = messages[data.status] || '...'; |
||||||
|
if (data.status == 'success') msg += data.ip; |
||||||
|
if (data.status == 'fail') msg += data.cause; |
||||||
|
|
||||||
|
$("#status").html(msg); |
||||||
|
|
||||||
|
if (done) { |
||||||
|
// $('#backbtn').removeClass('hidden'); |
||||||
|
$('.anim-dots').addClass('hidden'); |
||||||
|
} else { |
||||||
|
window.setTimeout(getStatus, 1000); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
abortTmeo = setTimeout(function () { |
||||||
|
xhr.abort(); |
||||||
|
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>);
|
||||||
|
// $('#backbtn').removeClass('hidden'); |
||||||
|
$('.anim-dots').addClass('hidden'); |
||||||
|
}, 4000); |
||||||
|
|
||||||
|
xhr.send(); |
||||||
|
} |
||||||
|
|
||||||
|
getStatus(); |
||||||
|
</script> |
@ -0,0 +1,37 @@ |
|||||||
|
<script> |
||||||
|
// Workaround for badly loaded page |
||||||
|
setTimeout(function() { |
||||||
|
if (typeof termInit == 'undefined' || typeof $ == 'undefined') { |
||||||
|
location.reload(true); |
||||||
|
} |
||||||
|
}, 2000); |
||||||
|
</script> |
||||||
|
|
||||||
|
<h1>%term_title%</h1> |
||||||
|
|
||||||
|
<div id="termwrap"> |
||||||
|
<div id="screen"></div> |
||||||
|
|
||||||
|
<div id="buttons"> |
||||||
|
<button data-n="1" class="btn-blue">%btn1%</button><!-- |
||||||
|
--><button data-n="2" class="btn-blue">%btn2%</button><!-- |
||||||
|
--><button data-n="3" class="btn-blue">%btn3%</button><!-- |
||||||
|
--><button data-n="4" class="btn-blue">%btn4%</button><!-- |
||||||
|
--><button data-n="5" class="btn-blue">%btn5%</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<nav id="botnav"> |
||||||
|
<a href="<?= url('cfg_wifi') ?>"><?= tr('menu.settings') ?></a><!--
|
||||||
|
--><a href="<?= url('help') ?>">Help</a><!--
|
||||||
|
--><a href="<?= url('about') ?>">About</a>
|
||||||
|
</nav> |
||||||
|
|
||||||
|
<script> |
||||||
|
try { |
||||||
|
termInit(%screenData%); |
||||||
|
} catch(e) { |
||||||
|
console.error("Fail, reloading..."); |
||||||
|
location.reload(true); |
||||||
|
} |
||||||
|
</script> |
File diff suppressed because one or more lines are too long
@ -1,178 +0,0 @@ |
|||||||
html { |
|
||||||
font-family: Arial, sans-serif; |
|
||||||
color: #D0D0D0; |
|
||||||
background: #131315; |
|
||||||
} |
|
||||||
|
|
||||||
html, body { |
|
||||||
@include naked(); |
|
||||||
width: 100%; |
|
||||||
height: 100%; |
|
||||||
overflow: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
a, a:visited, a:link { |
|
||||||
cursor: pointer; |
|
||||||
color: #5abfff; |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
a:hover { |
|
||||||
color: #5abfff; |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
.Box { |
|
||||||
display: block; |
|
||||||
max-width: 900px; |
|
||||||
|
|
||||||
margin-top: dist(0); |
|
||||||
padding: dist(-1) dist(0); |
|
||||||
|
|
||||||
@include media($phone) { |
|
||||||
margin-top: dist(-1); |
|
||||||
padding: dist(-3) dist(-2); |
|
||||||
} |
|
||||||
// |
|
||||||
//h1 + & { |
|
||||||
// margin-top: 0; |
|
||||||
//} |
|
||||||
// |
|
||||||
//h2 { |
|
||||||
// margin-top: 0; |
|
||||||
//} |
|
||||||
|
|
||||||
p:first-child { |
|
||||||
margin-top:0; |
|
||||||
} |
|
||||||
|
|
||||||
border-radius: 3px; |
|
||||||
background-color: rgba(white, .07); |
|
||||||
|
|
||||||
//&.wide { |
|
||||||
// width: initial; |
|
||||||
// max-width: initial; |
|
||||||
//} |
|
||||||
// |
|
||||||
//&.medium { |
|
||||||
// max-width: 1200px; |
|
||||||
//} |
|
||||||
// |
|
||||||
//.Valfield { |
|
||||||
// display: inline-block; |
|
||||||
// min-width: 10em; |
|
||||||
//} |
|
||||||
} |
|
||||||
|
|
||||||
body { |
|
||||||
position: relative; |
|
||||||
|
|
||||||
padding: dist(0); |
|
||||||
@include media($phone) { |
|
||||||
padding: dist(-1); |
|
||||||
} |
|
||||||
|
|
||||||
overflow-y: auto; |
|
||||||
|
|
||||||
& > * { |
|
||||||
margin-left: auto; |
|
||||||
margin-right: auto; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
h1,h2 { |
|
||||||
@include noselect(); |
|
||||||
} |
|
||||||
|
|
||||||
h1 { |
|
||||||
text-align: center; |
|
||||||
font-size: fsize(6); |
|
||||||
margin-top: 0; |
|
||||||
margin-bottom: dist(0); |
|
||||||
|
|
||||||
@include media($phone) { |
|
||||||
font-size: fsize(3); |
|
||||||
margin-bottom: dist(-1); |
|
||||||
} |
|
||||||
|
|
||||||
@include media($tablet) { |
|
||||||
font-size: fsize(5); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
h2 { |
|
||||||
font-size: fsize(2); |
|
||||||
margin-bottom: dist(-1); |
|
||||||
//&:first-child{margin-top:0} |
|
||||||
} |
|
||||||
|
|
||||||
td, th { |
|
||||||
padding: dist(-2); |
|
||||||
white-space: nowrap; |
|
||||||
|
|
||||||
@include media($phone) { |
|
||||||
padding: dist(-3); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
tbody th { |
|
||||||
text-align: right; |
|
||||||
width: $form-label-w; |
|
||||||
color: $c-form-label-fg; |
|
||||||
|
|
||||||
@include media($phone) { |
|
||||||
width: auto; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
tbody td { |
|
||||||
input[type="text"], input[type="number"] { |
|
||||||
width: 10em; |
|
||||||
|
|
||||||
@include media($phone) { |
|
||||||
width: 8em; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Loader wheel in top right corner |
|
||||||
#loader { |
|
||||||
position: absolute; |
|
||||||
right: dist(1); |
|
||||||
top: dist(1); |
|
||||||
|
|
||||||
transition: opacity .2s; |
|
||||||
opacity: 0; |
|
||||||
|
|
||||||
@include media($phone) { |
|
||||||
top: dist(0); |
|
||||||
right: dist(0); |
|
||||||
} |
|
||||||
|
|
||||||
&.show { |
|
||||||
opacity:1; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ul > * { |
|
||||||
padding-top: .1em; |
|
||||||
padding-bottom: .1em; |
|
||||||
} |
|
||||||
|
|
||||||
#botnav { |
|
||||||
padding-top: 1.5em; |
|
||||||
text-align: center; |
|
||||||
|
|
||||||
a { |
|
||||||
padding: 0 dist(-2); |
|
||||||
text-decoration: underline; |
|
||||||
|
|
||||||
&, &:visited, &:link { |
|
||||||
color: #2e4d6e; |
|
||||||
} |
|
||||||
|
|
||||||
&:hover { |
|
||||||
color: #5abfff; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,36 @@ |
|||||||
|
html { |
||||||
|
font-family: Arial, sans-serif; |
||||||
|
color: #D0D0D0; |
||||||
|
background: #131315; |
||||||
|
} |
||||||
|
|
||||||
|
html, body { |
||||||
|
@include naked(); |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
a, a:visited, a:link { |
||||||
|
cursor: pointer; |
||||||
|
color: #5abfff; |
||||||
|
text-decoration: none; |
||||||
|
} |
||||||
|
|
||||||
|
a:hover { |
||||||
|
color: #5abfff; |
||||||
|
text-decoration: underline; |
||||||
|
} |
||||||
|
|
||||||
|
.hidden { |
||||||
|
display: none !important; |
||||||
|
} |
||||||
|
|
||||||
|
[onclick] { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
ul > * { |
||||||
|
padding-top: .2em; |
||||||
|
padding-bottom: .2em; |
||||||
|
} |
@ -0,0 +1,137 @@ |
|||||||
|
.Box { |
||||||
|
display: block; |
||||||
|
max-width: 900px; |
||||||
|
|
||||||
|
margin-top: dist(0); |
||||||
|
padding: dist(-1) dist(0); |
||||||
|
|
||||||
|
// clear floats |
||||||
|
&::after { |
||||||
|
content: ''; |
||||||
|
display: block; |
||||||
|
clear: both |
||||||
|
} |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
margin-top: dist(-1); |
||||||
|
} |
||||||
|
|
||||||
|
h1, h2 { |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
h1 + & { |
||||||
|
margin-top: 0; |
||||||
|
} |
||||||
|
|
||||||
|
h2 { |
||||||
|
margin-top: 0; |
||||||
|
margin-bottom: 0 !important; |
||||||
|
} |
||||||
|
|
||||||
|
p:last-child { |
||||||
|
margin-bottom: 0.5em; |
||||||
|
} |
||||||
|
|
||||||
|
border-radius: 3px; |
||||||
|
background-color: rgba(white, .07); |
||||||
|
box-shadow: 0 0 4px black; |
||||||
|
border: 1px solid #4f4f4f; |
||||||
|
|
||||||
|
&.wide { |
||||||
|
width: initial; |
||||||
|
max-width: initial; |
||||||
|
} |
||||||
|
|
||||||
|
&.medium { |
||||||
|
max-width: 1200px; |
||||||
|
} |
||||||
|
|
||||||
|
//.Valfield { |
||||||
|
// display: inline-block; |
||||||
|
// min-width: 10em; |
||||||
|
//} |
||||||
|
|
||||||
|
// Submit Top Right |
||||||
|
&.str { |
||||||
|
position: relative; |
||||||
|
.Row.buttons { |
||||||
|
position: absolute; |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
right: dist(0); |
||||||
|
top: 1.8em; |
||||||
|
margin: 1rem auto; |
||||||
|
} |
||||||
|
|
||||||
|
@include media($tablet-min) { |
||||||
|
right: 0; |
||||||
|
top: 0; |
||||||
|
margin-top: dist(-1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.str.mobopen .Row.buttons { |
||||||
|
top: 0; |
||||||
|
margin-top: dist(-1); |
||||||
|
} |
||||||
|
|
||||||
|
.Row.explain { |
||||||
|
max-width: 600px; margin-left: 0; |
||||||
|
@include media($phone) { |
||||||
|
margin-top: 60px; |
||||||
|
} |
||||||
|
} |
||||||
|
&.mobopen .Row.explain { |
||||||
|
margin-top: 12px; // default from .Row |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
margin-top: 18px; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
.Box.mobcol { |
||||||
|
h2 { |
||||||
|
position: relative; |
||||||
|
cursor: pointer; |
||||||
|
padding-right: 1.3rem; |
||||||
|
|
||||||
|
&::after { |
||||||
|
position: absolute; |
||||||
|
right: 0; |
||||||
|
content: '▸'; |
||||||
|
|
||||||
|
top:50%; |
||||||
|
font-size: 120%; |
||||||
|
font-weight: bold; |
||||||
|
transform: translate(0,-50%) rotate(90deg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.expanded h2::after { |
||||||
|
transform: translate(-25%,-50%) rotate(-90deg); |
||||||
|
margin-bottom: dist(0); |
||||||
|
} |
||||||
|
|
||||||
|
.Row { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
#ap-box { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
&.expanded { |
||||||
|
.Row { |
||||||
|
display: flex; |
||||||
|
} |
||||||
|
|
||||||
|
#ap-box { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
#content { |
||||||
|
flex-grow: 1; |
||||||
|
position: relative; |
||||||
|
|
||||||
|
padding: dist(0); |
||||||
|
@include media($phone) { |
||||||
|
padding: dist(-1); |
||||||
|
} |
||||||
|
|
||||||
|
overflow-y: auto; |
||||||
|
|
||||||
|
& > * { |
||||||
|
margin-left: auto; |
||||||
|
margin-right: auto; |
||||||
|
} |
||||||
|
|
||||||
|
h1 { |
||||||
|
text-align: center; |
||||||
|
font-size: fsize(7); |
||||||
|
margin-top: 0; |
||||||
|
margin-bottom: dist(0); |
||||||
|
} |
||||||
|
|
||||||
|
h2 { |
||||||
|
font-size: fsize(3); |
||||||
|
margin-bottom: dist(-1); |
||||||
|
} |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
h1 { |
||||||
|
font-size: fsize(5); |
||||||
|
margin-bottom: dist(-1); |
||||||
|
} |
||||||
|
|
||||||
|
h2 { |
||||||
|
font-size: fsize(2); |
||||||
|
margin-bottom: dist(-1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
td, th { |
||||||
|
padding: dist(-2); |
||||||
|
} |
||||||
|
|
||||||
|
tbody th { |
||||||
|
text-align: right; |
||||||
|
width: $form-label-w; |
||||||
|
color: $c-form-label-fg; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
@import "base"; |
||||||
|
|
||||||
|
@import "outer-wrap"; |
||||||
|
@import "menu"; |
||||||
|
@import "content"; |
||||||
|
@import "loader"; |
||||||
|
|
||||||
|
@import "box"; |
||||||
|
@import "modal"; |
@ -0,0 +1,18 @@ |
|||||||
|
// Loader wheel in top right corner |
||||||
|
#loader { |
||||||
|
position: absolute; |
||||||
|
right: dist(1); |
||||||
|
top: dist(1); |
||||||
|
|
||||||
|
transition: opacity .2s; |
||||||
|
opacity: 0; |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
top: dist(0); |
||||||
|
right: dist(0); |
||||||
|
} |
||||||
|
|
||||||
|
&.show { |
||||||
|
opacity:1; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
#menu { |
||||||
|
$menu-bg: #3983CD; |
||||||
|
$menu-hl: #5badff; //#1bd886; |
||||||
|
$menu-fg: white; |
||||||
|
|
||||||
|
flex: 0 0 15rem; |
||||||
|
background: $menu-bg; |
||||||
|
|
||||||
|
& > * { |
||||||
|
display: block; |
||||||
|
text-decoration: none; |
||||||
|
padding: dist(-1) dist(0); |
||||||
|
|
||||||
|
@include nowrap; |
||||||
|
@include noselect; |
||||||
|
} |
||||||
|
|
||||||
|
#brand { |
||||||
|
color: $menu-fg; |
||||||
|
background: darken($menu-bg, 10%); |
||||||
|
font-size: 120%; |
||||||
|
text-align: center; |
||||||
|
position:relative; |
||||||
|
transition: none; |
||||||
|
font-weight: bold; |
||||||
|
|
||||||
|
margin-bottom: dist(0); |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
background: $menu-bg; |
||||||
|
cursor: pointer; |
||||||
|
margin-bottom: dist(-2); |
||||||
|
|
||||||
|
&::after { |
||||||
|
position: absolute; |
||||||
|
color: rgba(black, .4); |
||||||
|
right: dist(0); |
||||||
|
content: '▸'; |
||||||
|
|
||||||
|
top:50%; |
||||||
|
font-size: 120%; |
||||||
|
font-weight: bold; |
||||||
|
transform: translate(0,-50%) rotate(90deg); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
&.expanded #brand { |
||||||
|
background: darken($menu-bg, 10%); |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
&:after { transform: translate(-25%,-50%) rotate(-90deg) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
a { |
||||||
|
font-size: 130%; |
||||||
|
color: $menu-fg; |
||||||
|
|
||||||
|
transition: background-color 0.2s; |
||||||
|
text-shadow: 0 0 5px rgba(black, .4); |
||||||
|
|
||||||
|
&:hover, &.selected { |
||||||
|
background: $menu-hl; |
||||||
|
text-shadow: 0 0 5px rgba(black, .6); |
||||||
|
} |
||||||
|
|
||||||
|
&.selected { |
||||||
|
position: relative; |
||||||
|
box-shadow: 0 0 5px rgba(black, .5); |
||||||
|
} |
||||||
|
|
||||||
|
&:focus { |
||||||
|
outline-color: $c-red-outline; |
||||||
|
} |
||||||
|
|
||||||
|
//&::before { |
||||||
|
// content: "▸"; |
||||||
|
// padding-right: .5rem; |
||||||
|
// position: relative; |
||||||
|
// top: -0.1rem; |
||||||
|
//} |
||||||
|
|
||||||
|
// Fontello |
||||||
|
&::before { |
||||||
|
vertical-align: -2px; |
||||||
|
margin-left: 0; |
||||||
|
margin-right: 15px; |
||||||
|
} |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
display: none; |
||||||
|
&::before {margin-left: 10px;} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.expanded a { display:block } |
||||||
|
|
||||||
|
@include media($tablet) { |
||||||
|
#brand { |
||||||
|
font-size: 95%; |
||||||
|
margin-bottom: dist(-1); |
||||||
|
} |
||||||
|
|
||||||
|
a { font-size: 105%; } |
||||||
|
|
||||||
|
flex-basis: 10rem; |
||||||
|
|
||||||
|
& > * { padding: dist(-2) dist(-1); } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
/* Main outer container */ |
||||||
|
#outer { |
||||||
|
display: flex; |
||||||
|
|
||||||
|
position: absolute; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
top: 0; |
||||||
|
bottom: 0; |
||||||
|
overflow: hidden; |
||||||
|
|
||||||
|
flex-direction: row; |
||||||
|
} |
||||||
|
|
||||||
|
@include media($phone) { |
||||||
|
#outer { |
||||||
|
display: block; |
||||||
|
overflow-y: scroll; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
|
||||||
|
// Utilities for background tiling |
||||||
|
|
||||||
|
// Use a tile as background (w, h - size of time) |
||||||
|
@mixin tile_xy($w, $h, $x, $y) { |
||||||
|
background-position: (-$x*$w) (-$y*$h); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Use a square tile as background (size - w & h of time) |
||||||
|
@mixin tile($size, $x, $y) { |
||||||
|
@include tile_xy($size, $size, $x, $y); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Button with sprite-sheet |
||||||
|
// A B |
||||||
|
// B:hover B:hover |
||||||
|
@mixin tile_btn_h($w, $h, $x) { |
||||||
|
@include tile_xy($w, $h, $x, 0); |
||||||
|
&:hover { |
||||||
|
@include tile_xy($w, $h, $x, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// active the same as hover |
||||||
|
@mixin tile_btn_h_act($w, $h, $x) { |
||||||
|
@include tile_xy($w, $h, $x, 0); |
||||||
|
&:hover, &.active { |
||||||
|
@include tile_xy($w, $h, $x, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Button with sprite-sheet |
||||||
|
// A A:hover |
||||||
|
// B B:hover |
||||||
|
@mixin tile_btn_v($w, $h, $y) { |
||||||
|
@include tile_xy($w, $h, 0, $y); |
||||||
|
&:hover { |
||||||
|
@include tile_xy($w, $h, 1, $y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// active the same as hover |
||||||
|
@mixin tile_btn_v_act($w, $h, $y) { |
||||||
|
@include tile_xy($w, $h, 0, $y); |
||||||
|
&:hover, &.active { |
||||||
|
@include tile_xy($w, $h, 1, $y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@mixin inset-shadow-top($w, $c) { |
||||||
|
box-shadow: inset 0 $w ($w*2) (-$w) $c; |
||||||
|
} |
||||||
|
|
||||||
|
@mixin inset-shadow-bottom($w, $c) { |
||||||
|
box-shadow: inset 0 (-$w) ($w*2) (-$w) $c; |
||||||
|
} |
||||||
|
|
||||||
|
@mixin inset-shadow-left($w, $c) { |
||||||
|
box-shadow: inset $w 0 ($w*2) (-$w) $c; |
||||||
|
} |
||||||
|
|
||||||
|
@mixin inset-shadow-right($w, $c) { |
||||||
|
box-shadow: inset (-$w) 0 ($w*2) (-$w) $c; |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
@import "background-tiling"; |
||||||
|
@import "pointer"; |
||||||
|
@import "misc"; |
@ -0,0 +1,34 @@ |
|||||||
|
// Add a highlight for debugging |
||||||
|
@mixin highlight($color) { |
||||||
|
outline: 1px solid $color; |
||||||
|
background: rgba($color, .05); |
||||||
|
box-shadow: 0 0 2px 2px rgba($color, .2), inset 0 0 2px 2px rgba($color, .2); |
||||||
|
} |
||||||
|
|
||||||
|
// Ellipsis, but for block elements |
||||||
|
@mixin block-ellipsis($width: 100%) { |
||||||
|
display: block; |
||||||
|
max-width: $width; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
white-space: nowrap; |
||||||
|
word-wrap: normal; |
||||||
|
} |
||||||
|
|
||||||
|
// No margins, padding, borders |
||||||
|
@mixin naked() { |
||||||
|
border: 0 none; |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
text-decoration: none; |
||||||
|
} |
||||||
|
|
||||||
|
@mixin translate($x, $y) { |
||||||
|
@include transform(translate($x, $y)); |
||||||
|
} |
||||||
|
|
||||||
|
// Disallow wrapping |
||||||
|
@mixin nowrap() { |
||||||
|
white-space: nowrap; |
||||||
|
word-wrap: normal; |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
|
||||||
|
@mixin click-through() { |
||||||
|
pointer-events: none; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Disallow text selection |
||||||
|
@mixin noselect() { |
||||||
|
-webkit-user-select: none; |
||||||
|
-khtml-user-select: none; |
||||||
|
-moz-user-select: none; |
||||||
|
-ms-user-select: none; |
||||||
|
user-select: none; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Allow text selection |
||||||
|
@mixin can-select() { |
||||||
|
-webkit-user-select: text; |
||||||
|
-khtml-user-select: text; |
||||||
|
-moz-user-select: text; |
||||||
|
-ms-user-select: text; |
||||||
|
user-select: text; |
||||||
|
|
||||||
|
cursor: text; |
||||||
|
} |
@ -1,3 +1,3 @@ |
|||||||
#!/bin/bash |
#!/bin/bash |
||||||
|
|
||||||
xterm -e "php -S localhost:2000" |
xterm -e "php -S 0.0.0.0:2000" |
||||||
|
@ -1,53 +0,0 @@ |
|||||||
<!DOCTYPE html> |
|
||||||
<html> |
|
||||||
<head> |
|
||||||
<meta charset="UTF-8"> |
|
||||||
<title>ESP8266 Remote Terminal</title> |
|
||||||
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"> |
|
||||||
|
|
||||||
<script> |
|
||||||
// Workaround for badly loaded page |
|
||||||
setTimeout(function() { |
|
||||||
if (typeof termInit == 'undefined' || typeof $ == 'undefined') { |
|
||||||
location.reload(true); |
|
||||||
} |
|
||||||
}, 2000); |
|
||||||
</script> |
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/app.css"> |
|
||||||
<script src="/js/app.js"></script> |
|
||||||
</head> |
|
||||||
<body class="page-term"> |
|
||||||
|
|
||||||
<h1 onclick="location.href='/wifi'">ESP8266 Remote Terminal</h1> |
|
||||||
|
|
||||||
<div id="termwrap"> |
|
||||||
<div id="screen"></div> |
|
||||||
|
|
||||||
<div id="buttons"> |
|
||||||
<button data-n="1" class="btn-blue">1</button><!-- |
|
||||||
--><button data-n="2" class="btn-blue">2</button><!-- |
|
||||||
--><button data-n="3" class="btn-blue">3</button><!-- |
|
||||||
--><button data-n="4" class="btn-blue">4</button><!-- |
|
||||||
--><button data-n="5" class="btn-blue">5</button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<nav id="botnav"> |
|
||||||
<a href="/wifi">WiFi config</a><!-- |
|
||||||
--><a href="/help">Help</a><!-- |
|
||||||
--><a href="/about">About</a> |
|
||||||
</nav> |
|
||||||
|
|
||||||
<script> |
|
||||||
try { |
|
||||||
_root = window.location.host; |
|
||||||
termInit(%screenData%); |
|
||||||
} catch(e) { |
|
||||||
console.error("Fail, reloading..."); |
|
||||||
location.reload(true); |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
</body> |
|
||||||
</html> |
|
@ -1,17 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
require '_test_env.php'; |
|
||||||
|
|
||||||
$f = file_get_contents('term.html'); |
|
||||||
|
|
||||||
$f = str_replace('%screenData%', |
|
||||||
'{ |
|
||||||
"w": 26, "h": 10, |
|
||||||
"x": 0, "y": 0, |
|
||||||
"cv": 1, |
|
||||||
"screen": "70 t259" |
|
||||||
}', $f); |
|
||||||
|
|
||||||
$f = str_replace('window.location.host', json_encode(ESP_IP), $f); |
|
||||||
|
|
||||||
echo $f; |
|
@ -1,89 +0,0 @@ |
|||||||
<!doctype html> |
|
||||||
<html> |
|
||||||
<head> |
|
||||||
<meta charset="utf-8"> |
|
||||||
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"> |
|
||||||
|
|
||||||
<title>WiFi Settings - ESP8266 Remote Terminal</title> |
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/app.css"> |
|
||||||
<script src="/js/app.js"></script> |
|
||||||
</head> |
|
||||||
<body class="page-wifi"> |
|
||||||
|
|
||||||
<img src="/img/loader.gif" alt="Loading…" id="loader"> |
|
||||||
|
|
||||||
<h1 onclick="location.href='/'">WiFi settings</h1> |
|
||||||
|
|
||||||
<div class="Box" id="wificonfbox"> |
|
||||||
<table> |
|
||||||
<tr> |
|
||||||
<th>WiFi mode</th> |
|
||||||
<td id="opmodebox">%WiFiMode%</td> |
|
||||||
</tr> |
|
||||||
<tr class="x-hide-noip x-hide-2"> |
|
||||||
<th>IP</th> |
|
||||||
<td>%StaIP%</td> |
|
||||||
</tr> |
|
||||||
<tr> |
|
||||||
<th>Switch to</th> |
|
||||||
<td id="modeswitch"></td> |
|
||||||
</tr> |
|
||||||
<tr class="x-hide-1"> |
|
||||||
<th><label for="channel">AP channel</label></th> |
|
||||||
<td> |
|
||||||
<form action="/wifi/setchannel" method="GET"> |
|
||||||
<input name="ch" id="channel" type="number" step=1 min=1 max=14 value="%WiFiChannel%"><!-- |
|
||||||
--><input type="submit" value="Set" class="narrow btn-green x-hide-3"> |
|
||||||
</form> |
|
||||||
</td> |
|
||||||
</tr> |
|
||||||
<tr class="x-hide-1"> |
|
||||||
<th><label for="channel">AP name</label></th> |
|
||||||
<td> |
|
||||||
<form action="/wifi/setname" method="GET"> |
|
||||||
<input name="name" type="text" value="%APName%"><!-- |
|
||||||
--><input type="submit" value="Set" class="narrow btn-green"> |
|
||||||
</form> |
|
||||||
</td> |
|
||||||
</tr> |
|
||||||
<tr><td colspan=2 style="white-space: normal;"> |
|
||||||
<p>Some changes require a reboot, dropping connection. It can take a while to re-connect.</p> |
|
||||||
<p> |
|
||||||
<b>If you lose access</b>, hold the BOOT button for 2 seconds (the Tx LED starts blinking) to re-enable AP mode. |
|
||||||
If that fails, hold the BOOT button for over 5 seconds (rapid Tx LED flashing) to perform a factory reset. |
|
||||||
<p> |
|
||||||
</td></tr> |
|
||||||
</table> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="Box" id="ap-box"> |
|
||||||
<h2>Select AP to join</h2> |
|
||||||
<div id="ap-loader" class="x-hide-2">Scanning<span class="anim-dots">.</span></div> |
|
||||||
<div id="ap-noscan" class="x-hide-1 x-hide-3">Can't scan in AP-only mode.</div> |
|
||||||
<div id="ap-list" style="display:none"></div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<nav id="botnav"> |
|
||||||
<a href="/">Terminal</a><!-- |
|
||||||
--><a href="/help">Help</a><!-- |
|
||||||
--><a href="/about">About</a> |
|
||||||
</nav> |
|
||||||
|
|
||||||
<div class="Modal hidden" id="psk-modal"> |
|
||||||
<div class="Dialog"> |
|
||||||
<form action="/wifi/connect" method="post" id="conn-form"> |
|
||||||
<input type="hidden" id="conn-essid" name="essid"><!-- |
|
||||||
--><label for="conn-passwd">Password:</label><!-- |
|
||||||
--><input type="password" id="conn-passwd" name="passwd"><!-- |
|
||||||
--><input type="submit" value="Connect!"> |
|
||||||
</form> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<script> |
|
||||||
_root = window.location.host; |
|
||||||
wifiInit({staSSID: '%StaSSID%', staIP: '%StaIP%', mode: '%WiFiModeNum%'}); |
|
||||||
</script> |
|
||||||
</body> |
|
||||||
</html> |
|
@ -1,26 +0,0 @@ |
|||||||
<!DOCTYPE html> |
|
||||||
<html> |
|
||||||
<head> |
|
||||||
<meta charset="utf-8"> |
|
||||||
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"> |
|
||||||
|
|
||||||
<title>Connecting…</title> |
|
||||||
|
|
||||||
<link rel="stylesheet" href="css/app.css"> |
|
||||||
<script src="js/app.js"></script> |
|
||||||
</head> |
|
||||||
<body> |
|
||||||
|
|
||||||
<h1>Connecting to network</h1> |
|
||||||
|
|
||||||
<div class="Box"> |
|
||||||
<p><b>Status:</b><br><span id="status"></span><span class="anim-dots">.</span></p> |
|
||||||
<a href="/wifi" id="backbtn" class="hidden button">Back to WiFi config</a> |
|
||||||
</div> |
|
||||||
|
|
||||||
<script> |
|
||||||
_root = window.location.host; |
|
||||||
wifiConn(); |
|
||||||
</script> |
|
||||||
</body> |
|
||||||
</html> |
|
@ -1,15 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
require '_test_env.php'; |
|
||||||
|
|
||||||
$f = file_get_contents('wifi.html'); |
|
||||||
|
|
||||||
$f = str_replace('%StaSSID%', 'Chlivek', $f); |
|
||||||
$f = str_replace('%StaIP%', json_encode(ESP_IP), $f); |
|
||||||
$f = str_replace('%WiFiModeNum%', '1', $f); |
|
||||||
$f = str_replace('%WiFiMode%', 'Client', $f); |
|
||||||
$f = str_replace('%WiFiChannel%', '1', $f); |
|
||||||
|
|
||||||
$f = str_replace('window.location.host', json_encode(ESP_IP), $f); |
|
||||||
|
|
||||||
echo $f; |
|
@ -0,0 +1,25 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/07/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ESP_VT100_FIRMWARE_HELPERS_H |
||||||
|
#define ESP_VT100_FIRMWARE_HELPERS_H |
||||||
|
|
||||||
|
// strcpy that adds 0 at the end of the buffer. Returns void.
|
||||||
|
#define strncpy_safe(dst, src, n) do { strncpy((char *)(dst), (char *)(src), (n)); (dst)[(n)-1]=0; } while (0) |
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert IP hex to arguments for printf. |
||||||
|
* Library IP2STR(ip) does not work correctly due to unaligned memory access. |
||||||
|
*/ |
||||||
|
#define GOOD_IP2STR(ip) ((ip)>>0)&0xff, ((ip)>>8)&0xff, ((ip)>>16)&0xff, ((ip)>>24)&0xff |
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper that retrieves an arg from `connData->getArgs` and stores it in `buff`. Returns 1 on success |
||||||
|
*/ |
||||||
|
#define GET_ARG(key) (httpdFindArg(connData->getArgs, key, buff, sizeof(buff)) > 0) |
||||||
|
|
||||||
|
#define STR_HELPER(x) #x |
||||||
|
#define STR(x) STR_HELPER(x) |
||||||
|
|
||||||
|
#endif //ESP_VT100_FIRMWARE_HELPERS_H
|
@ -1 +1 @@ |
|||||||
Subproject commit 03003ea591a272df50159ba52f84ca84c5cad78e |
Subproject commit f3dd1a25993775bec062a1906ced7b07a7fc9db1 |
@ -0,0 +1,178 @@ |
|||||||
|
/*
|
||||||
|
Cgi/template routines for configuring non-wifi settings |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
#include "cgi_appcfg.h" |
||||||
|
#include "persist.h" |
||||||
|
#include "screen.h" |
||||||
|
#include "helpers.h" |
||||||
|
|
||||||
|
#define SET_REDIR_SUC "/cfg/app" |
||||||
|
#define SET_REDIR_ERR SET_REDIR_SUC"?err=" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Universal CGI endpoint to set Terminal params. |
||||||
|
*/ |
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR |
||||||
|
cgiAppCfgSetParams(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
char buff[50]; |
||||||
|
|
||||||
|
char redir_url_buf[300]; |
||||||
|
char *redir_url = redir_url_buf; |
||||||
|
redir_url += sprintf(redir_url, SET_REDIR_ERR); |
||||||
|
// we'll test if anything was printed by looking for \0 in failed_keys_buf
|
||||||
|
|
||||||
|
if (connData->conn == NULL) { |
||||||
|
//Connection aborted. Clean up.
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
// width and height must always go together so we can do max size validation
|
||||||
|
if (GET_ARG("term_width")) { |
||||||
|
dbg("Default screen width: %s", buff); |
||||||
|
int w = atoi(buff); |
||||||
|
if (w > 1) { |
||||||
|
if (GET_ARG("term_height")) { |
||||||
|
dbg("Default screen height: %s", buff); |
||||||
|
int h = atoi(buff); |
||||||
|
if (h > 1) { |
||||||
|
if (w * h <= MAX_SCREEN_SIZE) { |
||||||
|
termconf->width = w; |
||||||
|
termconf->height = h; |
||||||
|
} else { |
||||||
|
warn("Bad dimensions: %d x %d (total %d)", w, h, w*h); |
||||||
|
redir_url += sprintf(redir_url, "term_width,term_height,"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad height: \"%s\"", buff); |
||||||
|
redir_url += sprintf(redir_url, "term_width,"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Missing height arg!"); |
||||||
|
// this wont happen normally when the form is used
|
||||||
|
redir_url += sprintf(redir_url, "term_width,term_height,"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad width: \"%s\"", buff); |
||||||
|
redir_url += sprintf(redir_url, "term_width,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("default_bg")) { |
||||||
|
dbg("Screen default BG: %s", buff); |
||||||
|
int color = atoi(buff); |
||||||
|
if (color >= 0 && color < 16) { |
||||||
|
termconf->default_bg = (u8) color; |
||||||
|
} else { |
||||||
|
warn("Bad color %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "default_bg,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("default_fg")) { |
||||||
|
dbg("Screen default FG: %s", buff); |
||||||
|
int color = atoi(buff); |
||||||
|
if (color >= 0 && color < 16) { |
||||||
|
termconf->default_fg = (u8) color; |
||||||
|
} else { |
||||||
|
warn("Bad color %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "default_fg,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("term_title")) { |
||||||
|
dbg("Terminal title default text: \"%s\"", buff); |
||||||
|
strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in
|
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("btn1")) { |
||||||
|
dbg("Button1 default text: \"%s\"", buff); |
||||||
|
strncpy_safe(termconf->btn1, buff, 10); |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("btn2")) { |
||||||
|
dbg("Button1 default text: \"%s\"", buff); |
||||||
|
strncpy_safe(termconf->btn2, buff, 10); |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("btn3")) { |
||||||
|
dbg("Button1 default text: \"%s\"", buff); |
||||||
|
strncpy_safe(termconf->btn3, buff, 10); |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("btn4")) { |
||||||
|
dbg("Button1 default text: \"%s\"", buff); |
||||||
|
strncpy_safe(termconf->btn4, buff, 10); |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("btn5")) { |
||||||
|
dbg("Button1 default text: \"%s\"", buff); |
||||||
|
strncpy_safe(termconf->btn5, buff, 10); |
||||||
|
} |
||||||
|
|
||||||
|
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { |
||||||
|
// All was OK
|
||||||
|
info("Set app params - success, saving..."); |
||||||
|
|
||||||
|
terminal_apply_settings(); |
||||||
|
persist_store(); |
||||||
|
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC); |
||||||
|
} else { |
||||||
|
warn("Some settings did not validate, asking for correction"); |
||||||
|
// Some errors, appended to the URL as ?err=
|
||||||
|
httpdRedirect(connData, redir_url_buf); |
||||||
|
} |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR |
||||||
|
tplAppCfg(HttpdConnData *connData, char *token, void **arg) |
||||||
|
{ |
||||||
|
#define BUFLEN 100 |
||||||
|
char buff[BUFLEN]; |
||||||
|
|
||||||
|
if (token == NULL) { |
||||||
|
// We're done
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(buff, ""); // fallback
|
||||||
|
|
||||||
|
if (streq(token, "term_width")) { |
||||||
|
sprintf(buff, "%d", termconf->width); |
||||||
|
} |
||||||
|
else if (streq(token, "term_height")) { |
||||||
|
sprintf(buff, "%d", termconf->height); |
||||||
|
} |
||||||
|
else if (streq(token, "default_bg")) { |
||||||
|
sprintf(buff, "%d", termconf->default_bg); |
||||||
|
} |
||||||
|
else if (streq(token, "default_fg")) { |
||||||
|
sprintf(buff, "%d", termconf->default_fg); |
||||||
|
} |
||||||
|
else if (streq(token, "term_title")) { |
||||||
|
strncpy_safe(buff, termconf->title, BUFLEN); |
||||||
|
} |
||||||
|
else if (streq(token, "btn1")) { |
||||||
|
strncpy_safe(buff, termconf->btn1, BUFLEN); |
||||||
|
} |
||||||
|
else if (streq(token, "btn2")) { |
||||||
|
strncpy_safe(buff, termconf->btn2, BUFLEN); |
||||||
|
} |
||||||
|
else if (streq(token, "btn3")) { |
||||||
|
strncpy_safe(buff, termconf->btn3, BUFLEN); |
||||||
|
} |
||||||
|
else if (streq(token, "btn4")) { |
||||||
|
strncpy_safe(buff, termconf->btn4, BUFLEN); |
||||||
|
} |
||||||
|
else if (streq(token, "btn5")) { |
||||||
|
strncpy_safe(buff, termconf->btn5, BUFLEN); |
||||||
|
} |
||||||
|
|
||||||
|
httpdSend(connData, buff, -1); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
#ifndef CGIAPPCFG_H |
||||||
|
#define CGIAPPCFG_H |
||||||
|
|
||||||
|
#include "httpd.h" |
||||||
|
|
||||||
|
httpd_cgi_state cgiAppCfgSetParams(HttpdConnData *connData); |
||||||
|
httpd_cgi_state tplAppCfg(HttpdConnData *connData, char *token, void **arg); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,251 @@ |
|||||||
|
/*
|
||||||
|
configuring the network settings |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
#include "cgi_network.h" |
||||||
|
#include "wifimgr.h" |
||||||
|
#include "persist.h" |
||||||
|
#include "helpers.h" |
||||||
|
|
||||||
|
#define SET_REDIR_SUC "/cfg/network" |
||||||
|
#define SET_REDIR_ERR SET_REDIR_SUC"?err=" |
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for async timer |
||||||
|
*/ |
||||||
|
static void ICACHE_FLASH_ATTR applyNetSettingsLaterCb(void *arg) |
||||||
|
{ |
||||||
|
wifimgr_apply_settings(); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Universal CGI endpoint to set network params. |
||||||
|
* Those affect DHCP etc, may cause a disconnection. |
||||||
|
*/ |
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
static ETSTimer timer; |
||||||
|
|
||||||
|
char buff[50]; |
||||||
|
|
||||||
|
char redir_url_buf[300]; |
||||||
|
char *redir_url = redir_url_buf; |
||||||
|
redir_url += sprintf(redir_url, SET_REDIR_ERR); |
||||||
|
// we'll test if anything was printed by looking for \0 in failed_keys_buf
|
||||||
|
|
||||||
|
if (connData->conn == NULL) { |
||||||
|
//Connection aborted. Clean up.
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
// ---- AP DHCP server lease time ----
|
||||||
|
|
||||||
|
if (GET_ARG("ap_dhcp_time")) { |
||||||
|
dbg("Setting DHCP lease time to: %s min.", buff); |
||||||
|
int min = atoi(buff); |
||||||
|
if (min >= 1 && min <= 2880) { |
||||||
|
if (wificonf->ap_dhcp_time != min) { |
||||||
|
wificonf->ap_dhcp_time = (u16) min; |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Lease time %s out of allowed range 1-2880.", buff); |
||||||
|
redir_url += sprintf(redir_url, "ap_dhcp_time,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- AP DHCP start and end IP ----
|
||||||
|
|
||||||
|
if (GET_ARG("ap_dhcp_start")) { |
||||||
|
dbg("Setting DHCP range start IP to: \"%s\"", buff); |
||||||
|
u32 ip = ipaddr_addr(buff); |
||||||
|
if (ip != 0) { |
||||||
|
if (wificonf->ap_dhcp_range.start_ip.addr != ip) { |
||||||
|
wificonf->ap_dhcp_range.start_ip.addr = ip; |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad IP: %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "ap_dhcp_start,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("ap_dhcp_end")) { |
||||||
|
dbg("Setting DHCP range end IP to: \"%s\"", buff); |
||||||
|
u32 ip = ipaddr_addr(buff); |
||||||
|
if (ip != 0) { |
||||||
|
if (wificonf->ap_dhcp_range.end_ip.addr != ip) { |
||||||
|
wificonf->ap_dhcp_range.end_ip.addr = ip; |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad IP: %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "ap_dhcp_end,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- AP local address & config ----
|
||||||
|
|
||||||
|
if (GET_ARG("ap_addr_ip")) { |
||||||
|
dbg("Setting AP local IP to: \"%s\"", buff); |
||||||
|
u32 ip = ipaddr_addr(buff); |
||||||
|
if (ip != 0) { |
||||||
|
if (wificonf->ap_addr.ip.addr != ip) { |
||||||
|
wificonf->ap_addr.ip.addr = ip; |
||||||
|
wificonf->ap_addr.gw.addr = ip; // always the same, we're the router here
|
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad IP: %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "ap_addr_ip,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("ap_addr_mask")) { |
||||||
|
dbg("Setting AP local IP netmask to: \"%s\"", buff); |
||||||
|
u32 ip = ipaddr_addr(buff); |
||||||
|
if (ip != 0) { |
||||||
|
if (wificonf->ap_addr.netmask.addr != ip) { |
||||||
|
// ideally this should be checked to match the IP.
|
||||||
|
// Let's hope users know what they're doing
|
||||||
|
wificonf->ap_addr.netmask.addr = ip; |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad IP mask: %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "ap_addr_mask,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- Station enable/disable DHCP ----
|
||||||
|
|
||||||
|
// DHCP enable / disable (disable means static IP is enabled)
|
||||||
|
if (GET_ARG("sta_dhcp_enable")) { |
||||||
|
dbg("DHCP enable = %s", buff); |
||||||
|
int enable = atoi(buff); |
||||||
|
if (wificonf->sta_dhcp_enable != enable) { |
||||||
|
wificonf->sta_dhcp_enable = (bool)enable; |
||||||
|
wifi_change_flags.sta = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- Station IP config (Static IP) ----
|
||||||
|
|
||||||
|
if (GET_ARG("sta_addr_ip")) { |
||||||
|
dbg("Setting Station mode static IP to: \"%s\"", buff); |
||||||
|
u32 ip = ipaddr_addr(buff); |
||||||
|
if (ip != 0) { |
||||||
|
if (wificonf->sta_addr.ip.addr != ip) { |
||||||
|
wificonf->sta_addr.ip.addr = ip; |
||||||
|
wifi_change_flags.sta = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad IP: %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "sta_addr_ip,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("sta_addr_mask")) { |
||||||
|
dbg("Setting Station mode static IP netmask to: \"%s\"", buff); |
||||||
|
u32 ip = ipaddr_addr(buff); |
||||||
|
if (ip != 0 && ip != 0xFFFFFFFFUL) { |
||||||
|
if (wificonf->sta_addr.netmask.addr != ip) { |
||||||
|
wificonf->sta_addr.netmask.addr = ip; |
||||||
|
wifi_change_flags.sta = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad IP mask: %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "sta_addr_mask,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("sta_addr_gw")) { |
||||||
|
dbg("Setting Station mode static IP default gateway to: \"%s\"", buff); |
||||||
|
u32 ip = ipaddr_addr(buff); |
||||||
|
if (ip != 0) { |
||||||
|
if (wificonf->sta_addr.gw.addr != ip) { |
||||||
|
wificonf->sta_addr.gw.addr = ip; |
||||||
|
wifi_change_flags.sta = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad gw IP: %s", buff); |
||||||
|
redir_url += sprintf(redir_url, "sta_addr_gw,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { |
||||||
|
// All was OK
|
||||||
|
info("Set network params - success, applying in 1000 ms"); |
||||||
|
|
||||||
|
// Settings are applied only if all was OK
|
||||||
|
persist_store(); |
||||||
|
|
||||||
|
// Delayed settings apply, so the response page has a chance to load.
|
||||||
|
// If user connects via the Station IF, they may not even notice the connection reset.
|
||||||
|
os_timer_disarm(&timer); |
||||||
|
os_timer_setfn(&timer, applyNetSettingsLaterCb, NULL); |
||||||
|
os_timer_arm(&timer, 1000, false); |
||||||
|
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC); |
||||||
|
} else { |
||||||
|
warn("Some WiFi settings did not validate, asking for correction"); |
||||||
|
// Some errors, appended to the URL as ?err=
|
||||||
|
httpdRedirect(connData, redir_url_buf); |
||||||
|
} |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//Template code for the WLAN page.
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg) |
||||||
|
{ |
||||||
|
char buff[100]; |
||||||
|
u8 mac[6]; |
||||||
|
|
||||||
|
if (token == NULL) { |
||||||
|
// We're done
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(buff, ""); // fallback
|
||||||
|
|
||||||
|
if (streq(token, "ap_dhcp_time")) { |
||||||
|
sprintf(buff, "%d", wificonf->ap_dhcp_time); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_dhcp_start")) { |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr)); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_dhcp_end")) { |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr)); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_addr_ip")) { |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr)); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_addr_mask")) { |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr)); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_dhcp_enable")) { |
||||||
|
sprintf(buff, "%d", wificonf->sta_dhcp_enable); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_addr_ip")) { |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr)); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_addr_mask")) { |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr)); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_addr_gw")) { |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr)); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_mac")) { |
||||||
|
wifi_get_macaddr(STATION_IF, mac); |
||||||
|
sprintf(buff, MACSTR, MAC2STR(mac)); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_mac")) { |
||||||
|
wifi_get_macaddr(SOFTAP_IF, mac); |
||||||
|
sprintf(buff, MACSTR, MAC2STR(mac)); |
||||||
|
} |
||||||
|
|
||||||
|
httpdSend(connData, buff, -1); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
#ifndef CGINET_H |
||||||
|
#define CGINET_H |
||||||
|
|
||||||
|
#include "httpd.h" |
||||||
|
|
||||||
|
httpd_cgi_state cgiNetworkSetParams(HttpdConnData *connData); |
||||||
|
httpd_cgi_state tplNetwork(HttpdConnData *connData, char *token, void **arg); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,74 @@ |
|||||||
|
/*
|
||||||
|
Cgi/template routines for configuring non-wifi settings |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
#include "cgi_persist.h" |
||||||
|
#include "persist.h" |
||||||
|
#include "helpers.h" |
||||||
|
|
||||||
|
#define SET_REDIR_SUC "/cfg/admin" |
||||||
|
|
||||||
|
static bool ICACHE_FLASH_ATTR |
||||||
|
verify_admin_pw(const char *pw) |
||||||
|
{ |
||||||
|
// This is not really for security, but to prevent someone who
|
||||||
|
// shouldn't touch those settings from fucking it up.
|
||||||
|
return streq(pw, STR(ADMIN_PASSWORD)); // the PW comes from the makefile
|
||||||
|
} |
||||||
|
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR |
||||||
|
cgiPersistWriteDefaults(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
char buff[50]; |
||||||
|
|
||||||
|
if (connData->conn == NULL) { |
||||||
|
//Connection aborted. Clean up.
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
// width and height must always go together so we can do max size validation
|
||||||
|
if (GET_ARG("pw")) { |
||||||
|
dbg("Entered password for admin: %s", buff); |
||||||
|
if (verify_admin_pw(buff)) { |
||||||
|
dbg("pw is OK"); |
||||||
|
|
||||||
|
persist_set_as_default(); |
||||||
|
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
// if pw failed, show the same error as if it's wrong
|
||||||
|
} |
||||||
|
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC "?err=Password"); // this will show in the "validation errors" box
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR |
||||||
|
cgiPersistRestoreDefaults(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
if (connData->conn == NULL) { |
||||||
|
//Connection aborted. Clean up.
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
persist_restore_default(); |
||||||
|
httpdRedirect(connData, SET_REDIR_SUC); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR |
||||||
|
cgiPersistRestoreHard(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
if (connData->conn == NULL) { |
||||||
|
//Connection aborted. Clean up.
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
persist_restore_hard_default(); |
||||||
|
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
#ifndef CGIPERSIST_H |
||||||
|
#define CGIPERSIST_H |
||||||
|
|
||||||
|
#include "httpd.h" |
||||||
|
|
||||||
|
httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData); |
||||||
|
httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData); |
||||||
|
httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,585 @@ |
|||||||
|
/*
|
||||||
|
Cgi/template routines for the /wifi url. |
||||||
|
*/ |
||||||
|
|
||||||
|
/*
|
||||||
|
* ---------------------------------------------------------------------------- |
||||||
|
* "THE BEER-WARE LICENSE" (Revision 42): |
||||||
|
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
|
||||||
|
* this notice you can do whatever you want with this stuff. If we meet some day,
|
||||||
|
* and you think this stuff is worth it, you can buy me a beer in return.
|
||||||
|
* ---------------------------------------------------------------------------- |
||||||
|
* |
||||||
|
* File adapted and improved by Ondřej Hruška <ondra@ondrovo.com> |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
#include "cgi_wifi.h" |
||||||
|
#include "wifimgr.h" |
||||||
|
#include "persist.h" |
||||||
|
#include "helpers.h" |
||||||
|
|
||||||
|
#define SET_REDIR_SUC "/cfg/wifi" |
||||||
|
#define SET_REDIR_ERR SET_REDIR_SUC"?err=" |
||||||
|
|
||||||
|
/** WiFi access point data */ |
||||||
|
typedef struct { |
||||||
|
char ssid[32]; |
||||||
|
char bssid[8]; |
||||||
|
int channel; |
||||||
|
char rssi; |
||||||
|
char enc; |
||||||
|
} ApData; |
||||||
|
|
||||||
|
/** Scan result type */ |
||||||
|
typedef struct { |
||||||
|
char scanInProgress; //if 1, don't access the underlying stuff from the webpage.
|
||||||
|
ApData **apData; |
||||||
|
int noAps; |
||||||
|
} ScanResultData; |
||||||
|
|
||||||
|
/** Static scan status storage. */ |
||||||
|
static ScanResultData cgiWifiAps; |
||||||
|
|
||||||
|
/** Connection to AP periodic check timer */ |
||||||
|
static os_timer_t staCheckTimer; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate approximate signal strength % from RSSI |
||||||
|
*/ |
||||||
|
int ICACHE_FLASH_ATTR rssi2perc(int rssi) |
||||||
|
{ |
||||||
|
int r; |
||||||
|
|
||||||
|
if (rssi > 200) |
||||||
|
r = 100; |
||||||
|
else if (rssi < 100) |
||||||
|
r = 0; |
||||||
|
else |
||||||
|
r = 100 - 2 * (200 - rssi); // approx.
|
||||||
|
|
||||||
|
if (r > 100) r = 100; |
||||||
|
if (r < 0) r = 0; |
||||||
|
|
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert Auth type to string |
||||||
|
*/ |
||||||
|
const ICACHE_FLASH_ATTR char *auth2str(AUTH_MODE auth) |
||||||
|
{ |
||||||
|
switch (auth) { |
||||||
|
case AUTH_OPEN: |
||||||
|
return "Open"; |
||||||
|
case AUTH_WEP: |
||||||
|
return "WEP"; |
||||||
|
case AUTH_WPA_PSK: |
||||||
|
return "WPA"; |
||||||
|
case AUTH_WPA2_PSK: |
||||||
|
return "WPA2"; |
||||||
|
case AUTH_WPA_WPA2_PSK: |
||||||
|
return "WPA/WPA2"; |
||||||
|
default: |
||||||
|
return "Unknown"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert WiFi opmode to string |
||||||
|
*/ |
||||||
|
const ICACHE_FLASH_ATTR char *opmode2str(WIFI_MODE opmode) |
||||||
|
{ |
||||||
|
switch (opmode) { |
||||||
|
case NULL_MODE: |
||||||
|
return "Disabled"; |
||||||
|
case STATION_MODE: |
||||||
|
return "Client"; |
||||||
|
case SOFTAP_MODE: |
||||||
|
return "AP only"; |
||||||
|
case STATIONAP_MODE: |
||||||
|
return "Client+AP"; |
||||||
|
default: |
||||||
|
return "Unknown"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback the code calls when a wlan ap scan is done. Basically stores the result in |
||||||
|
* the static cgiWifiAps struct. |
||||||
|
* |
||||||
|
* @param arg - a pointer to {struct bss_info}, which is a linked list of the found APs |
||||||
|
* @param status - OK if the scan succeeded |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) |
||||||
|
{ |
||||||
|
int n; |
||||||
|
struct bss_info *bss_link = (struct bss_info *) arg; |
||||||
|
dbg("wifiScanDoneCb %d", status); |
||||||
|
if (status != OK) { |
||||||
|
cgiWifiAps.scanInProgress = 0; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Clear prev ap data if needed.
|
||||||
|
if (cgiWifiAps.apData != NULL) { |
||||||
|
for (n = 0; n < cgiWifiAps.noAps; n++) free(cgiWifiAps.apData[n]); |
||||||
|
free(cgiWifiAps.apData); |
||||||
|
} |
||||||
|
|
||||||
|
// Count amount of access points found.
|
||||||
|
n = 0; |
||||||
|
while (bss_link != NULL) { |
||||||
|
bss_link = bss_link->next.stqe_next; |
||||||
|
n++; |
||||||
|
} |
||||||
|
// Allocate memory for access point data
|
||||||
|
cgiWifiAps.apData = (ApData **) malloc(sizeof(ApData *) * n); |
||||||
|
if (cgiWifiAps.apData == NULL) { |
||||||
|
error("Out of memory allocating apData"); |
||||||
|
return; |
||||||
|
} |
||||||
|
cgiWifiAps.noAps = n; |
||||||
|
info("Scan done: found %d APs", n); |
||||||
|
|
||||||
|
// Copy access point data to the static struct
|
||||||
|
n = 0; |
||||||
|
bss_link = (struct bss_info *) arg; |
||||||
|
while (bss_link != NULL) { |
||||||
|
if (n >= cgiWifiAps.noAps) { |
||||||
|
// This means the bss_link changed under our nose. Shouldn't happen!
|
||||||
|
// Break because otherwise we will write in unallocated memory.
|
||||||
|
error("Huh? I have more than the allocated %d aps!", cgiWifiAps.noAps); |
||||||
|
break; |
||||||
|
} |
||||||
|
// Save the ap data.
|
||||||
|
cgiWifiAps.apData[n] = (ApData *) malloc(sizeof(ApData)); |
||||||
|
if (cgiWifiAps.apData[n] == NULL) { |
||||||
|
error("Can't allocate mem for ap buff."); |
||||||
|
cgiWifiAps.scanInProgress = 0; |
||||||
|
return; |
||||||
|
} |
||||||
|
cgiWifiAps.apData[n]->rssi = bss_link->rssi; |
||||||
|
cgiWifiAps.apData[n]->channel = bss_link->channel; |
||||||
|
cgiWifiAps.apData[n]->enc = bss_link->authmode; |
||||||
|
strncpy(cgiWifiAps.apData[n]->ssid, (char *) bss_link->ssid, 32); |
||||||
|
strncpy(cgiWifiAps.apData[n]->bssid, (char *) bss_link->bssid, 6); |
||||||
|
|
||||||
|
bss_link = bss_link->next.stqe_next; |
||||||
|
n++; |
||||||
|
} |
||||||
|
// We're done.
|
||||||
|
cgiWifiAps.scanInProgress = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Routine to start a WiFi access point scan. |
||||||
|
*/ |
||||||
|
static void ICACHE_FLASH_ATTR wifiStartScan(void) |
||||||
|
{ |
||||||
|
if (cgiWifiAps.scanInProgress) return; |
||||||
|
cgiWifiAps.scanInProgress = 1; |
||||||
|
wifi_station_scan(NULL, wifiScanDoneCb); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* This CGI is called from the bit of AJAX-code in wifi.tpl. It will initiate a |
||||||
|
* scan for access points and if available will return the result of an earlier scan. |
||||||
|
* The result is embedded in a bit of JSON parsed by the javascript in wifi.tpl. |
||||||
|
*/ |
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
int pos = (int) connData->cgiData; |
||||||
|
int len; |
||||||
|
char buff[256]; |
||||||
|
|
||||||
|
// 2nd and following runs of the function via MORE:
|
||||||
|
if (!cgiWifiAps.scanInProgress && pos != 0) { |
||||||
|
// Fill in json code for an access point
|
||||||
|
if (pos - 1 < cgiWifiAps.noAps) { |
||||||
|
int rssi = cgiWifiAps.apData[pos - 1]->rssi; |
||||||
|
|
||||||
|
len = sprintf(buff, "{\"essid\": \"%s\", \"bssid\": \"" |
||||||
|
MACSTR |
||||||
|
"\", \"rssi\": %d, \"rssi_perc\": %d, \"enc\": %d, \"channel\": %d}%s", |
||||||
|
cgiWifiAps.apData[pos - 1]->ssid, |
||||||
|
MAC2STR(cgiWifiAps.apData[pos - 1]->bssid), |
||||||
|
rssi, |
||||||
|
rssi2perc(rssi), |
||||||
|
cgiWifiAps.apData[pos - 1]->enc, |
||||||
|
cgiWifiAps.apData[pos - 1]->channel, |
||||||
|
(pos - 1 == cgiWifiAps.noAps - 1) ? "\n " : ",\n "); //<-terminator
|
||||||
|
|
||||||
|
httpdSend(connData, buff, len); |
||||||
|
} |
||||||
|
pos++; |
||||||
|
if ((pos - 1) >= cgiWifiAps.noAps) { |
||||||
|
len = sprintf(buff, " ]\n }\n}"); // terminate the whole object
|
||||||
|
httpdSend(connData, buff, len); |
||||||
|
// Also start a new scan.
|
||||||
|
wifiStartScan(); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
else { |
||||||
|
connData->cgiData = (void *) pos; |
||||||
|
return HTTPD_CGI_MORE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// First run of the function
|
||||||
|
httpdStartResponse(connData, 200); |
||||||
|
httpdHeader(connData, "Content-Type", "application/json"); |
||||||
|
httpdEndHeaders(connData); |
||||||
|
|
||||||
|
if (cgiWifiAps.scanInProgress == 1) { |
||||||
|
// We're still scanning. Tell Javascript code that.
|
||||||
|
len = sprintf(buff, "{\n \"result\": {\n \"inProgress\": 1\n }\n}"); |
||||||
|
httpdSend(connData, buff, len); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
else { |
||||||
|
// We have a scan result. Pass it on.
|
||||||
|
len = sprintf(buff, "{\n \"result\": {\n \"inProgress\": 0,\n \"APs\": [\n "); |
||||||
|
httpdSend(connData, buff, len); |
||||||
|
if (cgiWifiAps.apData == NULL) cgiWifiAps.noAps = 0; |
||||||
|
connData->cgiData = (void *) 1; |
||||||
|
return HTTPD_CGI_MORE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Cgi to get connection status. |
||||||
|
* |
||||||
|
* This endpoint returns JSON with keys: |
||||||
|
* - status = 'idle', 'working' or 'fail', |
||||||
|
* - ip = IP address, after connection succeeds |
||||||
|
*/ |
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
char buff[100]; |
||||||
|
struct ip_info info; |
||||||
|
|
||||||
|
httpdStartResponse(connData, 200); |
||||||
|
httpdHeader(connData, "Content-Type", "application/json"); |
||||||
|
httpdEndHeaders(connData); |
||||||
|
|
||||||
|
// if bad opmode or no SSID configured, skip any checks
|
||||||
|
if (!(wificonf->opmode & STATION_MODE) || wificonf->sta_ssid[0] == 0) { |
||||||
|
httpdSend(connData, "{\"status\": \"disabled\"}", -1); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
STATION_STATUS st = wifi_station_get_connect_status(); |
||||||
|
switch(st) { |
||||||
|
case STATION_IDLE: |
||||||
|
sprintf(buff, "{\"status\": \"idle\"}"); // unclear when this is used
|
||||||
|
break; |
||||||
|
|
||||||
|
case STATION_CONNECTING: |
||||||
|
sprintf(buff, "{\"status\": \"working\"}"); |
||||||
|
break; |
||||||
|
|
||||||
|
case STATION_WRONG_PASSWORD: |
||||||
|
sprintf(buff, "{\"status\": \"fail\", \"cause\": \"WRONG_PASSWORD\"}"); |
||||||
|
break; |
||||||
|
|
||||||
|
case STATION_NO_AP_FOUND: |
||||||
|
sprintf(buff, "{\"status\": \"fail\", \"cause\": \"AP_NOT_FOUND\"}"); |
||||||
|
break; |
||||||
|
|
||||||
|
case STATION_CONNECT_FAIL: |
||||||
|
sprintf(buff, "{\"status\": \"fail\", \"cause\": \"CONNECTION_FAILED\"}"); |
||||||
|
break; |
||||||
|
|
||||||
|
case STATION_GOT_IP: |
||||||
|
wifi_get_ip_info(STATION_IF, &info); |
||||||
|
sprintf(buff, "{\"status\": \"success\", \"ip\": \""IPSTR"\"}", GOOD_IP2STR(info.ip.addr)); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
httpdSend(connData, buff, -1); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for async timer |
||||||
|
*/ |
||||||
|
static void ICACHE_FLASH_ATTR applyWifiSettingsLaterCb(void *arg) |
||||||
|
{ |
||||||
|
(void*)arg; |
||||||
|
wifimgr_apply_settings(); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Universal CGI endpoint to set WiFi params. |
||||||
|
* Note that some may cause a (delayed) restart. |
||||||
|
*/ |
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData) |
||||||
|
{ |
||||||
|
static ETSTimer timer; |
||||||
|
|
||||||
|
char buff[50]; |
||||||
|
|
||||||
|
char redir_url_buf[300]; |
||||||
|
char *redir_url = redir_url_buf; |
||||||
|
redir_url += sprintf(redir_url, SET_REDIR_ERR); |
||||||
|
// we'll test if anything was printed by looking for \0 in failed_keys_buf
|
||||||
|
|
||||||
|
if (connData->conn == NULL) { |
||||||
|
//Connection aborted. Clean up.
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
// ---- WiFi opmode ----
|
||||||
|
|
||||||
|
if (GET_ARG("opmode")) { |
||||||
|
dbg("Setting WiFi opmode to: %s", buff); |
||||||
|
int mode = atoi(buff); |
||||||
|
if (mode > NULL_MODE && mode < MAX_MODE) { |
||||||
|
wificonf->opmode = (WIFI_MODE) mode; |
||||||
|
} else { |
||||||
|
warn("Bad opmode value \"%s\"", buff); |
||||||
|
redir_url += sprintf(redir_url, "opmode,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("ap_enable")) { |
||||||
|
dbg("Enable AP: %s", buff); |
||||||
|
int enable = atoi(buff); |
||||||
|
|
||||||
|
if (enable) { |
||||||
|
wificonf->opmode |= SOFTAP_MODE; |
||||||
|
} else { |
||||||
|
wificonf->opmode &= ~SOFTAP_MODE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (GET_ARG("sta_enable")) { |
||||||
|
dbg("Enable STA: %s", buff); |
||||||
|
int enable = atoi(buff); |
||||||
|
|
||||||
|
if (enable) { |
||||||
|
wificonf->opmode |= STATION_MODE; |
||||||
|
} else { |
||||||
|
wificonf->opmode &= ~STATION_MODE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- AP transmit power ----
|
||||||
|
|
||||||
|
if (GET_ARG("tpw")) { |
||||||
|
dbg("Setting AP power to: %s", buff); |
||||||
|
int tpw = atoi(buff); |
||||||
|
if (tpw >= 0 && tpw <= 82) { // 0 actually isn't 0 but quite low. 82 is very strong
|
||||||
|
if (wificonf->tpw != tpw) { |
||||||
|
wificonf->tpw = (u8) tpw; |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("tpw %s out of allowed range 0-82.", buff); |
||||||
|
redir_url += sprintf(redir_url, "tpw,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- AP channel (applies in AP-only mode) ----
|
||||||
|
|
||||||
|
if (GET_ARG("ap_channel")) { |
||||||
|
info("ap_channel = %s", buff); |
||||||
|
int channel = atoi(buff); |
||||||
|
if (channel > 0 && channel < 15) { |
||||||
|
if (wificonf->ap_channel != channel) { |
||||||
|
wificonf->ap_channel = (u8) channel; |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad channel value \"%s\", allowed 1-14", buff); |
||||||
|
redir_url += sprintf(redir_url, "ap_channel,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- SSID name in AP mode ----
|
||||||
|
|
||||||
|
if (GET_ARG("ap_ssid")) { |
||||||
|
// Replace all invalid ASCII with underscores
|
||||||
|
int i; |
||||||
|
for (i = 0; i < 32; i++) { |
||||||
|
char c = buff[i]; |
||||||
|
if (c == 0) break; |
||||||
|
if (c < 32 || c >= 127) buff[i] = '_'; |
||||||
|
} |
||||||
|
buff[i] = 0; |
||||||
|
|
||||||
|
if (strlen(buff) > 0) { |
||||||
|
if (!streq(wificonf->ap_ssid, buff)) { |
||||||
|
info("Setting SSID to \"%s\"", buff); |
||||||
|
strncpy_safe(wificonf->ap_ssid, buff, SSID_LEN); |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad SSID len."); |
||||||
|
redir_url += sprintf(redir_url, "ap_ssid,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- AP password ----
|
||||||
|
|
||||||
|
if (GET_ARG("ap_password")) { |
||||||
|
// Users are free to use any stupid shit in ther password,
|
||||||
|
// but it may lock them out.
|
||||||
|
if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) { |
||||||
|
if (!streq(wificonf->ap_password, buff)) { |
||||||
|
info("Setting AP password to \"%s\"", buff); |
||||||
|
strncpy_safe(wificonf->ap_password, buff, PASSWORD_LEN); |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
warn("Bad password len."); |
||||||
|
redir_url += sprintf(redir_url, "ap_password,"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- Hide AP network (do not announce) ----
|
||||||
|
|
||||||
|
if (GET_ARG("ap_hidden")) { |
||||||
|
dbg("AP hidden = %s", buff); |
||||||
|
int hidden = atoi(buff); |
||||||
|
if (hidden != wificonf->ap_hidden) { |
||||||
|
wificonf->ap_hidden = (hidden != 0); |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- Station SSID (to connect to) ----
|
||||||
|
|
||||||
|
if (GET_ARG("sta_ssid")) { |
||||||
|
if (!streq(wificonf->sta_ssid, buff)) { |
||||||
|
// No verification needed, at worst it fails to connect
|
||||||
|
info("Setting station SSID to: \"%s\"", buff); |
||||||
|
strncpy_safe(wificonf->sta_ssid, buff, SSID_LEN); |
||||||
|
wifi_change_flags.sta = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ---- Station password (empty for none is allowed) ----
|
||||||
|
|
||||||
|
if (GET_ARG("sta_password")) { |
||||||
|
if (!streq(wificonf->sta_password, buff)) { |
||||||
|
// No verification needed, at worst it fails to connect
|
||||||
|
info("Setting station password to: \"%s\"", buff); |
||||||
|
strncpy_safe(wificonf->sta_password, buff, PASSWORD_LEN); |
||||||
|
wifi_change_flags.sta = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) { |
||||||
|
// All was OK
|
||||||
|
info("Set WiFi params - success, applying in 1000 ms"); |
||||||
|
|
||||||
|
// Settings are applied only if all was OK
|
||||||
|
//
|
||||||
|
// This is so that options that consist of multiple keys sent together are not applied
|
||||||
|
// only partially if set wrong, which could lead to eg. user losing access and having
|
||||||
|
// to reset to defaults.
|
||||||
|
persist_store(); |
||||||
|
|
||||||
|
// Delayed settings apply, so the response page has a chance to load.
|
||||||
|
// If user connects via the Station IF, they may not even notice the connection reset.
|
||||||
|
os_timer_disarm(&timer); |
||||||
|
os_timer_setfn(&timer, applyWifiSettingsLaterCb, NULL); |
||||||
|
os_timer_arm(&timer, 1000, false); |
||||||
|
|
||||||
|
httpdRedirect(connData, SET_REDIR_SUC); |
||||||
|
} else { |
||||||
|
warn("Some WiFi settings did not validate, asking for correction"); |
||||||
|
// Some errors, appended to the URL as ?err=
|
||||||
|
httpdRedirect(connData, redir_url_buf); |
||||||
|
} |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//Template code for the WLAN page.
|
||||||
|
httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) |
||||||
|
{ |
||||||
|
char buff[100]; |
||||||
|
int x; |
||||||
|
int connectStatus; |
||||||
|
|
||||||
|
if (token == NULL) { |
||||||
|
// We're done
|
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
||||||
|
|
||||||
|
strcpy(buff, ""); // fallback
|
||||||
|
|
||||||
|
if (streq(token, "opmode_name")) { |
||||||
|
strcpy(buff, opmode2str(wificonf->opmode)); |
||||||
|
} |
||||||
|
else if (streq(token, "opmode")) { |
||||||
|
sprintf(buff, "%d", wificonf->opmode); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_enable")) { |
||||||
|
sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_enable")) { |
||||||
|
sprintf(buff, "%d", (wificonf->opmode & SOFTAP_MODE) != 0); |
||||||
|
} |
||||||
|
else if (streq(token, "tpw")) { |
||||||
|
sprintf(buff, "%d", wificonf->tpw); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_channel")) { |
||||||
|
sprintf(buff, "%d", wificonf->ap_channel); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_ssid")) { |
||||||
|
sprintf(buff, "%s", wificonf->ap_ssid); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_password")) { |
||||||
|
sprintf(buff, "%s", wificonf->ap_password); |
||||||
|
} |
||||||
|
else if (streq(token, "ap_hidden")) { |
||||||
|
sprintf(buff, "%d", wificonf->ap_hidden); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_ssid")) { |
||||||
|
sprintf(buff, "%s", wificonf->sta_ssid); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_password")) { |
||||||
|
sprintf(buff, "%s", wificonf->sta_password); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_rssi")) { |
||||||
|
sprintf(buff, "%d", wifi_station_get_rssi()); |
||||||
|
} |
||||||
|
else if (streq(token, "sta_active_ssid")) { |
||||||
|
// For display of our current SSID
|
||||||
|
connectStatus = wifi_station_get_connect_status(); |
||||||
|
x = wifi_get_opmode(); |
||||||
|
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) { |
||||||
|
strcpy(buff, ""); |
||||||
|
} |
||||||
|
else { |
||||||
|
struct station_config staconf; |
||||||
|
wifi_station_get_config(&staconf); |
||||||
|
strcpy(buff, (char *) staconf.ssid); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (streq(token, "sta_active_ip")) { |
||||||
|
x = wifi_get_opmode(); |
||||||
|
connectStatus = wifi_station_get_connect_status(); |
||||||
|
|
||||||
|
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) { |
||||||
|
strcpy(buff, ""); |
||||||
|
} |
||||||
|
else { |
||||||
|
struct ip_info info; |
||||||
|
wifi_get_ip_info(STATION_IF, &info); |
||||||
|
sprintf(buff, IPSTR, GOOD_IP2STR(info.ip.addr)); |
||||||
|
|
||||||
|
// sprintf(buff, "ip: "IPSTR", mask: "IPSTR", gw: "IPSTR,
|
||||||
|
// GOOD_IP2STR(info.ip.addr),
|
||||||
|
// GOOD_IP2STR(info.netmask.addr),
|
||||||
|
// GOOD_IP2STR(info.gw.addr));
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
httpdSend(connData, buff, -1); |
||||||
|
return HTTPD_CGI_DONE; |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
#ifndef CGIWIFI_H |
||||||
|
#define CGIWIFI_H |
||||||
|
|
||||||
|
#include "httpd.h" |
||||||
|
#include "helpers.h" |
||||||
|
|
||||||
|
httpd_cgi_state cgiWiFiScan(HttpdConnData *connData); |
||||||
|
|
||||||
|
httpd_cgi_state cgiWiFiSetParams(HttpdConnData *connData); |
||||||
|
httpd_cgi_state tplWlan(HttpdConnData *connData, char *token, void **arg); |
||||||
|
httpd_cgi_state cgiWiFiConnStatus(HttpdConnData *connData); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,171 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/07/09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "persist.h" |
||||||
|
#include <esp8266.h> |
||||||
|
#include "wifimgr.h" |
||||||
|
#include "screen.h" |
||||||
|
|
||||||
|
PersistBlock persist; |
||||||
|
|
||||||
|
#define PERSIST_SECTOR_ID 0x3D |
||||||
|
|
||||||
|
//region Persist and restore individual modules
|
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
apply_live_settings(void) |
||||||
|
{ |
||||||
|
dbg("[Persist] Applying live settings..."); |
||||||
|
terminal_apply_settings(); |
||||||
|
wifimgr_apply_settings(); |
||||||
|
// ...
|
||||||
|
} |
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
restore_live_settings_to_hard_defaults(void) |
||||||
|
{ |
||||||
|
wifimgr_restore_defaults(); |
||||||
|
terminal_restore_defaults(); |
||||||
|
// ...
|
||||||
|
} |
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute CRC32. Adapted from https://github.com/esp8266/Arduino
|
||||||
|
* @param data |
||||||
|
* @param length |
||||||
|
* @return crc32 |
||||||
|
*/ |
||||||
|
static uint32_t ICACHE_FLASH_ATTR |
||||||
|
calculateCRC32(const uint8_t *data, size_t length) |
||||||
|
{ |
||||||
|
uint32_t crc = 0xffffffff; |
||||||
|
while (length--) { |
||||||
|
uint8_t c = *data++; |
||||||
|
for (uint32_t i = 0x80; i > 0; i >>= 1) { |
||||||
|
bool bit = (bool) (crc & 0x80000000UL); |
||||||
|
if (c & i) { |
||||||
|
bit = !bit; |
||||||
|
} |
||||||
|
crc <<= 1; |
||||||
|
if (bit) { |
||||||
|
crc ^= 0x04c11db7UL; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return crc; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute a persist bundle checksum |
||||||
|
* |
||||||
|
* @param bundle |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
static uint32_t ICACHE_FLASH_ATTR |
||||||
|
compute_checksum(AppConfigBundle *bundle) |
||||||
|
{ |
||||||
|
return calculateCRC32((uint8_t *) bundle, sizeof(AppConfigBundle) - 4) ^ CHECKSUM_SALT; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Load, verify and apply persistent config |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
persist_load(void) |
||||||
|
{ |
||||||
|
info("[Persist] Loading stored settings from FLASH..."); |
||||||
|
|
||||||
|
dbg("sizeof(AppConfigBundle) = %d bytes", sizeof(AppConfigBundle)); |
||||||
|
dbg("sizeof(PersistBlock) = %d bytes", sizeof(PersistBlock)); |
||||||
|
dbg("sizeof(WiFiConfigBundle) = %d bytes", sizeof(WiFiConfigBundle)); |
||||||
|
dbg("sizeof(TerminalConfigBundle) = %d bytes", sizeof(TerminalConfigBundle)); |
||||||
|
|
||||||
|
bool hard_reset = false; |
||||||
|
|
||||||
|
// Try to load
|
||||||
|
hard_reset |= !system_param_load(PERSIST_SECTOR_ID, 0, &persist, sizeof(PersistBlock)); |
||||||
|
|
||||||
|
// Verify checksums
|
||||||
|
if (hard_reset || |
||||||
|
(compute_checksum(&persist.defaults) != persist.defaults.checksum) || |
||||||
|
(compute_checksum(&persist.current) != persist.current.checksum)) { |
||||||
|
error("[Persist] Checksum verification: FAILED"); |
||||||
|
hard_reset = true; |
||||||
|
} else { |
||||||
|
info("[Persist] Checksum verification: PASSED"); |
||||||
|
} |
||||||
|
|
||||||
|
if (hard_reset) { |
||||||
|
persist_restore_hard_default(); |
||||||
|
// this also stores them to flash and applies to modules
|
||||||
|
} else { |
||||||
|
apply_live_settings(); |
||||||
|
} |
||||||
|
|
||||||
|
info("[Persist] All settings loaded and applied."); |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
persist_store(void) |
||||||
|
{ |
||||||
|
info("[Persist] Storing all settings to FLASH..."); |
||||||
|
|
||||||
|
// Update checksums before write
|
||||||
|
persist.current.checksum = compute_checksum(&persist.current); |
||||||
|
persist.defaults.checksum = compute_checksum(&persist.defaults); |
||||||
|
|
||||||
|
if (!system_param_save_with_protect(PERSIST_SECTOR_ID, &persist, sizeof(PersistBlock))) { |
||||||
|
error("[Persist] Store to flash failed!"); |
||||||
|
} |
||||||
|
info("[Persist] All settings persisted."); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore to built-in defaults |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
persist_restore_hard_default(void) |
||||||
|
{ |
||||||
|
info("[Persist] Restoring all settings to hard defaults..."); |
||||||
|
|
||||||
|
// Set live config to default values
|
||||||
|
restore_live_settings_to_hard_defaults(); |
||||||
|
|
||||||
|
// Store current -> default
|
||||||
|
memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle)); |
||||||
|
persist_store(); |
||||||
|
|
||||||
|
info("[Persist] All settings restored to hard defaults."); |
||||||
|
|
||||||
|
apply_live_settings(); // apply
|
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore default settings & apply |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
persist_restore_default(void) |
||||||
|
{ |
||||||
|
info("[Persist] Restoring live settings to stored defaults..."); |
||||||
|
memcpy(&persist.current, &persist.defaults, sizeof(AppConfigBundle)); |
||||||
|
apply_live_settings(); |
||||||
|
info("[Persist] Settings restored to stored defaults."); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Store current settings as defaults & write to flash |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
persist_set_as_default(void) |
||||||
|
{ |
||||||
|
info("[Persist] Storing live settings as defaults.."); |
||||||
|
|
||||||
|
// current -> defaults
|
||||||
|
memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle)); |
||||||
|
persist_store(); |
||||||
|
|
||||||
|
info("[Persist] Default settings updated."); |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/07/09.
|
||||||
|
//
|
||||||
|
// There are 4 sets of settings.
|
||||||
|
// - hard defaults - hardcoded in firmware, used for init defaults after flash or if stored data are corrupt
|
||||||
|
// - defaults - persisted by privileged user
|
||||||
|
// - current - persistent current config state, can be restored to defaults any time
|
||||||
|
// - live - non-persistent settings valid only for the current runtime
|
||||||
|
|
||||||
|
#ifndef ESP_VT100_FIRMWARE_PERSIST_H |
||||||
|
#define ESP_VT100_FIRMWARE_PERSIST_H |
||||||
|
|
||||||
|
#include "wifimgr.h" |
||||||
|
#include "screen.h" |
||||||
|
|
||||||
|
// Changing this could be used to force-erase the config area
|
||||||
|
// after a firmware upgrade
|
||||||
|
#define CHECKSUM_SALT 0x5F5F5F5F |
||||||
|
|
||||||
|
/** Struct for current or default settings */ |
||||||
|
typedef struct { |
||||||
|
WiFiConfigBundle wificonf; |
||||||
|
TerminalConfigBundle termconf; |
||||||
|
|
||||||
|
// --- Space for future settings ---
|
||||||
|
// Original size: 1024
|
||||||
|
//
|
||||||
|
// The size must be appropriately reduced each time something is added,
|
||||||
|
// and boolean flags defaulting to 0 should be used to detect unpopulated
|
||||||
|
// sections that must be restored to defaults on load.
|
||||||
|
//
|
||||||
|
// This ensures user settings are not lost each time they upgrade the firmware,
|
||||||
|
// which would lead to a checksum mismatch if the structure was changed and
|
||||||
|
// it grew to a different memory area.
|
||||||
|
uint8_t filler[1024]; |
||||||
|
|
||||||
|
uint32_t checksum; // computed before write and tested on load. If it doesn't match, values are reset to hard defaults.
|
||||||
|
} AppConfigBundle; |
||||||
|
|
||||||
|
/** This is the entire data block stored in FLASH */ |
||||||
|
typedef struct { |
||||||
|
AppConfigBundle defaults; // defaults are stored here
|
||||||
|
AppConfigBundle current; // active settings adjusted by the user
|
||||||
|
} PersistBlock; |
||||||
|
|
||||||
|
// Persist holds the data currently loaded from the flash
|
||||||
|
extern PersistBlock persist; |
||||||
|
|
||||||
|
void persist_load(void); |
||||||
|
void persist_restore_hard_default(void); |
||||||
|
void persist_restore_default(void); |
||||||
|
void persist_set_as_default(void); |
||||||
|
void persist_store(void); |
||||||
|
|
||||||
|
#endif //ESP_VT100_FIRMWARE_PERSIST_H
|
@ -0,0 +1,197 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/07/08.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "wifimgr.h" |
||||||
|
#include "persist.h" |
||||||
|
|
||||||
|
WiFiConfigBundle * const wificonf = &persist.current.wificonf; |
||||||
|
WiFiConfChangeFlags wifi_change_flags; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore defaults in the WiFi config block. |
||||||
|
* This is to be called if the WiFi config is corrupted on startup, |
||||||
|
* before applying the config. |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
wifimgr_restore_defaults(void) |
||||||
|
{ |
||||||
|
u8 mac[6]; |
||||||
|
wifi_get_macaddr(SOFTAP_IF, mac); |
||||||
|
|
||||||
|
wificonf->opmode = SOFTAP_MODE; |
||||||
|
wificonf->tpw = 20; |
||||||
|
wificonf->ap_channel = 1; |
||||||
|
sprintf((char *) wificonf->ap_ssid, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]); |
||||||
|
wificonf->ap_password[0] = 0; // PSK2 always if password is not null.
|
||||||
|
wificonf->ap_hidden = false; |
||||||
|
|
||||||
|
IP4_ADDR(&wificonf->ap_addr.ip, 192, 168, 4, 1); |
||||||
|
IP4_ADDR(&wificonf->ap_addr.netmask, 255, 255, 255, 0); |
||||||
|
wificonf->ap_addr.gw.addr = wificonf->ap_addr.gw.addr; |
||||||
|
|
||||||
|
IP4_ADDR(&wificonf->ap_dhcp_range.start_ip, 192, 168, 4, 100); |
||||||
|
IP4_ADDR(&wificonf->ap_dhcp_range.end_ip, 192, 168, 4, 200); |
||||||
|
wificonf->ap_dhcp_range.enable = 1; // this will never get changed, idk why it's even there
|
||||||
|
wificonf->ap_dhcp_time = 120; |
||||||
|
|
||||||
|
// --- Client config ---
|
||||||
|
wificonf->sta_ssid[0] = 0; |
||||||
|
wificonf->sta_password[0] = 0; |
||||||
|
wificonf->sta_dhcp_enable = true; |
||||||
|
|
||||||
|
IP4_ADDR(&wificonf->sta_addr.ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw"
|
||||||
|
IP4_ADDR(&wificonf->sta_addr.netmask, 255, 255, 255, 0); |
||||||
|
IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1); |
||||||
|
|
||||||
|
// DEBUG ONLY - TODO remove for release
|
||||||
|
wificonf->opmode = STATION_MODE; |
||||||
|
sprintf((char*)wificonf->sta_ssid, "Chlivek"); |
||||||
|
sprintf((char*)wificonf->sta_password, "prase chrochta"); |
||||||
|
} |
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
configure_station(void) |
||||||
|
{ |
||||||
|
info("[WiFi] Configuring Station mode..."); |
||||||
|
struct station_config conf; |
||||||
|
strcpy((char *) conf.ssid, (char *) wificonf->sta_ssid); |
||||||
|
strcpy((char *) conf.password, (char *) wificonf->sta_password); |
||||||
|
dbg("[WiFi] Connecting to \"%s\", password \"%s\"", conf.ssid, conf.password); |
||||||
|
conf.bssid_set = 0; |
||||||
|
conf.bssid[0] = 0; |
||||||
|
wifi_station_disconnect(); |
||||||
|
wifi_station_set_config_current(&conf); |
||||||
|
|
||||||
|
if (wificonf->sta_dhcp_enable) { |
||||||
|
dbg("[WiFi] Starting DHCP..."); |
||||||
|
if (!wifi_station_dhcpc_start()) { |
||||||
|
error("[WiFi] DHCP failed to start!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
info("[WiFi] Setting up static IP..."); |
||||||
|
dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr)); |
||||||
|
dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr)); |
||||||
|
dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr)); |
||||||
|
|
||||||
|
wifi_station_dhcpc_stop(); |
||||||
|
// Load static IP config
|
||||||
|
if (!wifi_set_ip_info(STATION_IF, &wificonf->sta_addr)) { |
||||||
|
error("[WiFi] Error setting static IP!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
info("[WiFi] Trying to connect to AP..."); |
||||||
|
wifi_station_connect(); |
||||||
|
} |
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
configure_ap(void) |
||||||
|
{ |
||||||
|
bool suc; |
||||||
|
|
||||||
|
info("[WiFi] Configuring SoftAP mode..."); |
||||||
|
// AP is enabled
|
||||||
|
struct softap_config conf; |
||||||
|
conf.channel = wificonf->ap_channel; |
||||||
|
strcpy((char *) conf.ssid, (char *) wificonf->ap_ssid); |
||||||
|
strcpy((char *) conf.password, (char *) wificonf->ap_password); |
||||||
|
conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK); |
||||||
|
conf.ssid_len = (uint8_t) strlen((char *) conf.ssid); |
||||||
|
conf.ssid_hidden = wificonf->ap_hidden; |
||||||
|
conf.max_connection = 4; // default 4 (max possible)
|
||||||
|
conf.beacon_interval = 100; // default 100 ms
|
||||||
|
|
||||||
|
// Set config
|
||||||
|
//ETS_UART_INTR_DISABLE();
|
||||||
|
suc = wifi_softap_set_config_current(&conf); |
||||||
|
//ETS_UART_INTR_ENABLE();
|
||||||
|
if (!suc) { |
||||||
|
error("[WiFi] AP config set fail!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Set IP
|
||||||
|
info("[WiFi] Configuring SoftAP local IP..."); |
||||||
|
dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr)); |
||||||
|
dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr)); |
||||||
|
dbg("[WiFi] SoftAP.gw = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.gw.addr)); |
||||||
|
|
||||||
|
wifi_softap_dhcps_stop(); |
||||||
|
|
||||||
|
// Configure DHCP
|
||||||
|
if (!wifi_set_ip_info(SOFTAP_IF, &wificonf->ap_addr)) { |
||||||
|
error("[WiFi] IP set fail!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
info("[WiFi] Configuring SoftAP DHCP server..."); |
||||||
|
dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr)); |
||||||
|
dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr)); |
||||||
|
dbg("[WiFi] DHCP.lease = %d minutes", wificonf->ap_dhcp_time); |
||||||
|
|
||||||
|
if (!wifi_softap_set_dhcps_lease(&wificonf->ap_dhcp_range)) { |
||||||
|
error("[WiFi] DHCP address range set fail!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!wifi_softap_set_dhcps_lease_time(wificonf->ap_dhcp_time)) { |
||||||
|
error("[WiFi] DHCP lease time set fail!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// some weird magic shit about router
|
||||||
|
uint8 mode = 1; |
||||||
|
wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &mode); |
||||||
|
|
||||||
|
if (!wifi_softap_dhcps_start()) { |
||||||
|
error("[WiFi] Failed to start DHCP server!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the WiFi event listener, cycle WiFi, apply settings |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
wifimgr_apply_settings(void) |
||||||
|
{ |
||||||
|
info("[WiFi] Initializing..."); |
||||||
|
|
||||||
|
// Force wifi cycle
|
||||||
|
// Disconnect - may not be needed?
|
||||||
|
WIFI_MODE opmode = wifi_get_opmode(); |
||||||
|
|
||||||
|
bool is_sta = wificonf->opmode & STATION_MODE; |
||||||
|
bool is_ap = wificonf->opmode & SOFTAP_MODE; |
||||||
|
|
||||||
|
if ((wificonf->opmode & STATION_MODE) && !(opmode & STATION_MODE)) { |
||||||
|
wifi_change_flags.sta = true; |
||||||
|
} |
||||||
|
|
||||||
|
if ((wificonf->opmode & SOFTAP_MODE) && !(opmode & SOFTAP_MODE)) { |
||||||
|
wifi_change_flags.ap = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (opmode != wificonf->opmode) { |
||||||
|
wifi_set_opmode_current(wificonf->opmode); |
||||||
|
} |
||||||
|
|
||||||
|
// Configure the client
|
||||||
|
if (is_sta && wifi_change_flags.sta) { |
||||||
|
configure_station(); |
||||||
|
} |
||||||
|
|
||||||
|
// Configure the AP
|
||||||
|
if (is_ap && wifi_change_flags.ap) { |
||||||
|
configure_ap(); |
||||||
|
} |
||||||
|
|
||||||
|
wifi_change_flags.ap = false; |
||||||
|
wifi_change_flags.sta = false; |
||||||
|
|
||||||
|
info("[WiFi] WiFi settings applied."); |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/07/08.
|
||||||
|
// This module handles all WiFi configuration and is interfaced
|
||||||
|
// by the cgi_wifi functions.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ESP_VT100_FIRMWARE_WIFI_MANAGER_H |
||||||
|
#define ESP_VT100_FIRMWARE_WIFI_MANAGER_H |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
#include "cgi_wifi.h" |
||||||
|
|
||||||
|
#define SSID_LEN 32 |
||||||
|
#define PASSWORD_LEN 64 |
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure holding all configured WiFi parameters |
||||||
|
* and the active state. |
||||||
|
* |
||||||
|
* This block can be used eg. for WiFi config backup. |
||||||
|
*/ |
||||||
|
typedef struct { |
||||||
|
WIFI_MODE opmode : 8; |
||||||
|
u8 tpw; |
||||||
|
|
||||||
|
// --- AP config ---
|
||||||
|
u8 ap_channel; |
||||||
|
u8 ap_ssid[SSID_LEN]; |
||||||
|
u8 ap_password[PASSWORD_LEN]; |
||||||
|
bool ap_hidden; |
||||||
|
u16 ap_dhcp_time; // in minutes
|
||||||
|
struct dhcps_lease ap_dhcp_range; |
||||||
|
|
||||||
|
struct ip_info ap_addr; |
||||||
|
|
||||||
|
// --- Client config ---
|
||||||
|
u8 sta_ssid[SSID_LEN]; |
||||||
|
u8 sta_password[PASSWORD_LEN]; |
||||||
|
bool sta_dhcp_enable; |
||||||
|
|
||||||
|
struct ip_info sta_addr; |
||||||
|
} WiFiConfigBundle; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
bool sta; |
||||||
|
bool ap; |
||||||
|
} WiFiConfChangeFlags; |
||||||
|
|
||||||
|
extern WiFiConfChangeFlags wifi_change_flags; |
||||||
|
|
||||||
|
extern WiFiConfigBundle * const wificonf; |
||||||
|
|
||||||
|
void wifimgr_restore_defaults(void); |
||||||
|
|
||||||
|
void wifimgr_apply_settings(void); |
||||||
|
|
||||||
|
#endif //ESP_VT100_FIRMWARE_WIFI_MANAGER_H
|
Loading…
Reference in new issue