diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 4f3707ea..8d17537d 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -140,6 +140,7 @@ #define CUSTOM_RESET_MQTT 4 // Reset via MQTT #define CUSTOM_RESET_RPC 5 // Reset via RPC (HTTP) #define CUSTOM_RESET_OTA 6 // Reset after successful OTA update +#define CUSTOM_RESET_HTTP 7 // Reset via HTTP GET #define CUSTOM_RESET_NOFUSS 8 // Reset after successful NOFUSS update #define CUSTOM_RESET_UPGRADE 9 // Reset after update from web interface #define CUSTOM_RESET_FACTORY 10 // Factory reset from terminal @@ -154,13 +155,15 @@ PROGMEM const char custom_reset_terminal[] = "Reboot from terminal"; PROGMEM const char custom_reset_mqtt[] = "Reboot from MQTT"; PROGMEM const char custom_reset_rpc[] = "Reboot from RPC"; PROGMEM const char custom_reset_ota[] = "Reboot after successful OTA update"; +PROGMEM const char custom_reset_http[] = "Reboot from HTTP"; PROGMEM const char custom_reset_nofuss[] = "Reboot after successful NoFUSS update"; PROGMEM const char custom_reset_upgrade[] = "Reboot after successful web update"; PROGMEM const char custom_reset_factory[] = "Factory reset"; PROGMEM const char* const custom_reset_string[] = { custom_reset_hardware, custom_reset_web, custom_reset_terminal, custom_reset_mqtt, custom_reset_rpc, custom_reset_ota, - custom_reset_nofuss, custom_reset_upgrade, custom_reset_factory + custom_reset_http, custom_reset_nofuss, custom_reset_upgrade, + custom_reset_factory }; //------------------------------------------------------------------------------ diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index 38d549aa..c3bc6252 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -46,6 +46,7 @@ template bool setSetting(const String& key, T value); template bool setSetting(const String& key, unsigned int index, T value); template String getSetting(const String& key, T defaultValue); template String getSetting(const String& key, unsigned int index, T defaultValue); +bool settingsRestore(JsonObject& data); // ----------------------------------------------------------------------------- // Domoticz diff --git a/code/espurna/settings.ino b/code/espurna/settings.ino index 493c7b7f..b8b0f90f 100644 --- a/code/espurna/settings.ino +++ b/code/espurna/settings.ino @@ -90,6 +90,28 @@ String settingsKeyName(unsigned int index) { } +bool settingsRestore(JsonObject& data) { + + const char* app = data["app"]; + if (strcmp(app, APP_NAME) != 0) return false; + + for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) { + EEPROM.write(i, 0xFF); + } + + for (auto element : data) { + if (strcmp(element.key, "app") == 0) continue; + if (strcmp(element.key, "version") == 0) continue; + setSetting(element.key, element.value.as()); + } + + saveSettings(); + + DEBUG_MSG_P(PSTR("[SETTINGS] Settings restored successfully\n")); + return true; + +} + void settingsFactoryReset() { for (unsigned int i = 0; i < SPI_FLASH_SEC_SIZE; i++) { EEPROM.write(i, 0xFF); diff --git a/code/espurna/web.ino b/code/espurna/web.ino index ab847b40..43de5ea3 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -28,11 +28,18 @@ Copyright (C) 2016-2017 by Xose PĂ©rez AsyncWebServer * _server; char _last_modified[50]; +std::vector * _webConfigBuffer; +bool _webConfigSuccess = false; // ----------------------------------------------------------------------------- // HOOKS // ----------------------------------------------------------------------------- +void _onReset(AsyncWebServerRequest *request) { + deferredReset(100, CUSTOM_RESET_HTTP); + request->send(200); +} + void _onGetConfig(AsyncWebServerRequest *request) { webLog(request); @@ -59,6 +66,52 @@ void _onGetConfig(AsyncWebServerRequest *request) { } +void _onPostConfig(AsyncWebServerRequest *request) { + webLog(request); + if (!_authenticate(request)) return request->requestAuthentication(getSetting("hostname").c_str()); + request->send(_webConfigSuccess ? 200 : 400); +} + +void _onPostConfigData(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { + + // No buffer + if (final && (index == 0)) { + DynamicJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.parseObject((char *) data); + if (root.success()) _webConfigSuccess = settingsRestore(root); + return; + } + + // Buffer start => reset + if (index == 0) if (_webConfigBuffer) delete _webConfigBuffer; + + // init buffer if it doesn't exist + if (!_webConfigBuffer) { + _webConfigBuffer = new std::vector(); + _webConfigSuccess = false; + } + + // Copy + if (len > 0) { + _webConfigBuffer->reserve(_webConfigBuffer->size() + len); + _webConfigBuffer->insert(_webConfigBuffer->end(), data, data + len); + } + + // Ending + if (final) { + + _webConfigBuffer->push_back(0); + + // Parse JSON + DynamicJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.parseObject((char *) _webConfigBuffer->data()); + if (root.success()) _webConfigSuccess = settingsRestore(root); + delete _webConfigBuffer; + + } + +} + #if WEB_EMBEDDED void _onHome(AsyncWebServerRequest *request) { @@ -250,7 +303,9 @@ void webSetup() { #if WEB_EMBEDDED _server->on("/index.html", HTTP_GET, _onHome); #endif + _server->on("/reset", HTTP_GET, _onReset); _server->on("/config", HTTP_GET, _onGetConfig); + _server->on("/config", HTTP_POST | HTTP_PUT, _onPostConfig, _onPostConfigData); _server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData); // Serve static files diff --git a/code/espurna/ws.ino b/code/espurna/ws.ino index 9de9efb9..fbd6270d 100644 --- a/code/espurna/ws.ino +++ b/code/espurna/ws.ino @@ -55,7 +55,7 @@ bool _wsStore(String key, JsonArray& value) { bool changed = false; unsigned char index = 0; - for (auto element : (JsonArray&) value) { + for (auto element : value) { if (_wsStore(key + index, element.as())) changed = true; index++; } @@ -66,7 +66,6 @@ bool _wsStore(String key, JsonArray& value) { changed = true; } - if (changed) Serial.println(key); return changed; } @@ -89,51 +88,40 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { // Check actions ----------------------------------------------------------- - if (root.containsKey("action")) { + const char* action = root["action"]; + if (action) { - String action = root["action"]; - JsonObject& data = root.containsKey("data") ? root["data"] : jsonBuffer.createObject(); + DEBUG_MSG_P(PSTR("[WEBSOCKET] Requested action: %s\n"), action); - DEBUG_MSG_P(PSTR("[WEBSOCKET] Requested action: %s\n"), action.c_str()); + if (strcmp(action, "reboot") == 0) deferredReset(100, CUSTOM_RESET_WEB); + if (strcmp(action, "reconnect") == 0) _web_defer.once_ms(100, wifiDisconnect); - // Callbacks - for (unsigned char i = 0; i < _ws_on_action_callbacks.size(); i++) { - (_ws_on_action_callbacks[i])(action.c_str(), data); - } - - if (action.equals("reboot")) deferredReset(100, CUSTOM_RESET_WEB); - if (action.equals("reconnect")) _web_defer.once_ms(100, wifiDisconnect); - - if (action.equals("restore")) { + JsonObject& data = root["data"]; + if (data.success()) { - if (!data.containsKey("app") || (data["app"] != APP_NAME)) { - wsSend_P(client_id, PSTR("{\"message\": 4}")); - return; - } - - for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) { - EEPROM.write(i, 0xFF); + // Callbacks + for (unsigned char i = 0; i < _ws_on_action_callbacks.size(); i++) { + (_ws_on_action_callbacks[i])(action, data); } - for (auto element : data) { - if (strcmp(element.key, "app") == 0) continue; - if (strcmp(element.key, "version") == 0) continue; - setSetting(element.key, element.value.as()); + // Restore configuration via websockets + if (strcmp(action, "restore") == 0) { + if (settingsRestore(data)) { + wsSend_P(client_id, PSTR("{\"message\": 5}")); + } else { + wsSend_P(client_id, PSTR("{\"message\": 4}")); + } } - saveSettings(); - - wsSend_P(client_id, PSTR("{\"message\": 5}")); - } }; // Check configuration ----------------------------------------------------- - if (root.containsKey("config") && root["config"].is()) { + JsonObject& config = root["config"]; + if (config.success()) { - JsonObject& config = root["config"]; DEBUG_MSG_P(PSTR("[WEBSOCKET] Parsing configuration data\n")); String adminPass; @@ -179,7 +167,6 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { powerCalibrate(POWER_MAGNITUDE_ACTIVE, expected); save = true; } - delSetting(key); continue; } @@ -188,7 +175,6 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { powerCalibrate(POWER_MAGNITUDE_VOLTAGE, expected); save = true; } - delSetting(key); continue; } @@ -197,7 +183,6 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { powerCalibrate(POWER_MAGNITUDE_CURRENT, expected); save = true; } - delSetting(key); continue; } @@ -206,7 +191,6 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { powerCalibrate(POWER_MAGNITUDE_POWER_FACTOR, expected); save = true; } - delSetting(key); continue; } @@ -214,9 +198,7 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { if (value.as()) { powerResetCalibration(); save = true; - Serial.println(key); } - delSetting(key); continue; } @@ -273,10 +255,11 @@ void _wsParse(AsyncWebSocketClient *client, uint8_t * payload, size_t length) { void _wsOnStart(JsonObject& root) { - bool changePassword = false; #if WEB_FORCE_PASS_CHANGE String adminPass = getSetting("adminPass", ADMIN_PASS); - if (adminPass.equals(ADMIN_PASS)) changePassword = true; + bool changePassword = adminPass.equals(ADMIN_PASS); + #else + bool changePassword = false; #endif if (changePassword) {