diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 77da0758..4337c724 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -13,6 +13,7 @@ #define EEPROM_RELAY_STATUS 0 #define EEPROM_ENERGY_COUNT 1 +#define EEPROM_DATA_END 5 //-------------------------------------------------------------------------------- // BUTTON diff --git a/code/espurna/settings.ino b/code/espurna/settings.ino index 24cd117e..633126ea 100644 --- a/code/espurna/settings.ino +++ b/code/espurna/settings.ino @@ -20,18 +20,44 @@ Embedis embedis(Serial); // ----------------------------------------------------------------------------- unsigned long settingsSize() { - bool zero = false; - unsigned long size = 0; - for (unsigned int i = SPI_FLASH_SEC_SIZE; i>0; i--) { - size++; - if (EEPROM.read(i) == 0) { - if (zero) break; - zero = true; - } else { - zero = false; + unsigned pos = SPI_FLASH_SEC_SIZE - 1; + while (size_t len = EEPROM.read(pos)) { + pos = pos - len - 2; + } + return SPI_FLASH_SEC_SIZE - pos; +} + +unsigned int settingsKeyCount() { + unsigned count = 0; + unsigned pos = SPI_FLASH_SEC_SIZE - 1; + while (size_t len = EEPROM.read(pos)) { + pos = pos - len - 2; + count ++; + } + return count / 2; +} + +String settingsKeyName(unsigned int index) { + + String s; + + unsigned count = 0; + unsigned stop = index * 2 + 1; + unsigned pos = SPI_FLASH_SEC_SIZE - 1; + while (size_t len = EEPROM.read(pos)) { + pos = pos - len - 2; + count++; + if (count == stop) { + s.reserve(len); + for (unsigned char i = 0 ; i < len; i++) { + s += (char) EEPROM.read(pos + i + 1); + } + break; } } - return size-2; + + return s; + } void settingsSetup() { @@ -92,6 +118,16 @@ void settingsSetup() { e->response(String(settingsSize())); }); + Embedis::command( F("DUMP"), [](Embedis* e) { + unsigned int size = settingsKeyCount(); + for (unsigned int i=0; istream->printf("+%s => %s\n", key.c_str(), value.c_str()); + } + e->response(Embedis::OK); + }); + DEBUG_MSG("[SETTINGS] EEPROM size: %d bytes\n", SPI_FLASH_SEC_SIZE); DEBUG_MSG("[SETTINGS] Settings size: %d bytes\n", settingsSize()); diff --git a/code/espurna/web.ino b/code/espurna/web.ino index 4cacee4b..cffedb42 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -89,6 +89,22 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { DEBUG_MSG("[WEBSOCKET] Requested action: %s\n", action.c_str()); if (action.equals("reset")) ESP.reset(); + if (action.equals("restore") && root.containsKey("data")) { + + for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) { + EEPROM.write(i, 0xFF); + } + + JsonObject& data = root["data"]; + for (auto element : data){ + setSetting(element.key, element.value.as()); + } + + saveSettings(); + + ws.text(client_id, "{\"message\": \"Changes saved. You should reboot your board now.\"}"); + + } if (action.equals("reconnect")) { // Let the HTTP request return and disconnect after 100ms @@ -707,6 +723,29 @@ void _onAuth(AsyncWebServerRequest *request) { } +void _onGetConfig(AsyncWebServerRequest *request) { + + webLogRequest(request); + if (!_authenticate(request)) return request->requestAuthentication(); + + AsyncJsonResponse * response = new AsyncJsonResponse(); + JsonObject& root = response->getRoot(); + + unsigned int size = settingsKeyCount(); + for (unsigned int i=0; iaddHeader("Content-Disposition", buffer); + response->setLength(); + request->send(response); + +} + #if EMBEDDED_WEB void _onHome(AsyncWebServerRequest *request) { @@ -751,6 +790,7 @@ void webSetup() { #if EMBEDDED_WEB _server->on("/index.html", HTTP_GET, _onHome); #endif + _server->on("/config", HTTP_GET, _onGetConfig); _server->on("/auth", HTTP_GET, _onAuth); _server->on("/apis", HTTP_GET, _onAPIs); _server->on("/rpc", HTTP_GET, _onRPC); diff --git a/code/html/custom.css b/code/html/custom.css index e9196fa7..84f7a764 100644 --- a/code/html/custom.css +++ b/code/html/custom.css @@ -51,6 +51,10 @@ .button-more-network { background: rgb(223, 117, 20); } +.button-settings-backup, +.button-settings-restore { + background: rgb(0, 202, 0); +} .pure-g { margin-bottom: 20px; } diff --git a/code/html/custom.js b/code/html/custom.js index 47490894..643bc371 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -1,6 +1,8 @@ var websock; var password = false; var maxNetworks; +var host; +var port; // http://www.the-art-of-web.com/javascript/validate-password/ function checkPassword(str) { @@ -71,6 +73,44 @@ function doToggle(element, value) { return false; } +function backupSettings() { + document.getElementById('downloader').src = 'http://' + host + ':' + port + '/config'; + return false; +} + +function onFileUpload(event) { + + var inputFiles = this.files; + if (inputFiles == undefined || inputFiles.length == 0) return false; + var inputFile = inputFiles[0]; + + var response = window.confirm("Previous settings will be overwritten. Are you sure you want to restore this settings?"); + if (response == false) return false; + + var reader = new FileReader(); + reader.onload = function(e) { + var data = getJson(e.target.result); + if (data) { + websock.send(JSON.stringify({'action': 'restore', 'data': data})); + } else { + alert("The file is not a configuration backup or is corrupted"); + } + }; + reader.readAsText(inputFile); + + return false; + +} + +function restoreSettings() { + if (typeof window.FileReader !== 'function') { + alert("The file API isn't supported on this browser yet."); + } else { + $("#uploader").click(); + } + return false; +} + function randomString(length, chars) { var mask = ''; if (chars.indexOf('a') > -1) mask += 'abcdefghijklmnopqrstuvwxyz'; @@ -357,13 +397,17 @@ function getJson(str) { } } -function connect(host, port) { - if (typeof host === 'undefined') { - host = window.location.hostname; +function connect(h, p) { + + if (typeof h === 'undefined') { + h = window.location.hostname; } - if (typeof port === 'undefined') { - port = location.port; + if (typeof p === 'undefined') { + p = location.port; } + host = h; + port = p; + if (websock) websock.close(); websock = new WebSocket('ws://' + host + ':' + port + '/ws'); websock.onopen = function(evt) { @@ -388,6 +432,9 @@ function init() { $(".button-update-password").on('click', doUpdatePassword); $(".button-reset").on('click', doReset); $(".button-reconnect").on('click', doReconnect); + $(".button-settings-backup").on('click', backupSettings); + $(".button-settings-restore").on('click', restoreSettings); + $('#uploader').on('change', onFileUpload); $(".button-apikey").on('click', doGenerateAPIKey); $(".pure-menu-link").on('click', showPanel); $(".button-add-network").on('click', function() { diff --git a/code/html/index.html b/code/html/index.html index 53c81789..037e9336 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -294,16 +294,22 @@
-
+
-
+
Celsius (ºC)
Fahrenheit (ºF)
+
+ +
+
+
+ @@ -616,6 +622,9 @@ + + +