Factory reset feature (#114)

Implemented factory-reset feature
Extract factory settings into separate ini file
Hide reset/factory reset from guest user

Co-authored-by: kasedy <kasedy@gmail.com>
This commit is contained in:
rjwats
2020-05-20 00:32:49 +01:00
committed by GitHub
parent 51dabb705f
commit a59f32c420
31 changed files with 410 additions and 1592 deletions

View File

@ -15,8 +15,17 @@
#define DNS_PORT 53
#define AP_DEFAULT_SSID "ESP8266-React"
#define AP_DEFAULT_PASSWORD "esp-react"
#ifndef FACTORY_AP_SSID
#define FACTORY_AP_SSID "ESP8266-React"
#endif
#ifndef FACTORY_AP_PASSWORD
#define FACTORY_AP_PASSWORD "esp-react"
#endif
#ifndef FACTORY_AP_PROVISION_MODE
#define FACTORY_AP_PROVISION_MODE AP_MODE_DISCONNECTED
#endif
#define AP_SETTINGS_FILE "/config/apSettings.json"
#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings"
@ -34,7 +43,7 @@ class APSettings {
}
static void deserialize(JsonObject& root, APSettings& settings) {
settings.provisionMode = root["provision_mode"] | AP_MODE_ALWAYS;
settings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE;
switch (settings.provisionMode) {
case AP_MODE_ALWAYS:
case AP_MODE_DISCONNECTED:
@ -43,8 +52,8 @@ class APSettings {
default:
settings.provisionMode = AP_MODE_ALWAYS;
}
settings.ssid = root["ssid"] | AP_DEFAULT_SSID;
settings.password = root["password"] | AP_DEFAULT_PASSWORD;
settings.ssid = root["ssid"] | FACTORY_AP_SSID;
settings.password = root["password"] | FACTORY_AP_PASSWORD;
}
};

View File

@ -8,6 +8,7 @@ ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs) :
_otaSettingsService(server, fs, &_securitySettingsService),
_mqttSettingsService(server, fs, &_securitySettingsService),
_restartService(server, &_securitySettingsService),
_factoryResetService(server, fs, &_securitySettingsService),
_authenticationService(server, &_securitySettingsService),
_wifiScanner(server, &_securitySettingsService),
_wifiStatus(server, &_securitySettingsService),

View File

@ -16,6 +16,7 @@
#include <APSettingsService.h>
#include <APStatus.h>
#include <AuthenticationService.h>
#include <FactoryResetService.h>
#include <MqttSettingsService.h>
#include <MqttStatus.h>
#include <NTPSettingsService.h>
@ -71,6 +72,10 @@ class ESP8266React {
return _mqttSettingsService.getMqttClient();
}
void factoryReset() {
_factoryResetService.factoryReset();
}
private:
SecuritySettingsService _securitySettingsService;
WiFiSettingsService _wifiSettingsService;
@ -79,6 +84,7 @@ class ESP8266React {
OTASettingsService _otaSettingsService;
MqttSettingsService _mqttSettingsService;
RestartService _restartService;
FactoryResetService _factoryResetService;
AuthenticationService _authenticationService;

17
lib/framework/ESPUtils.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef ESPUtils_h
#define ESPUtils_h
#include <Arduino.h>
class ESPUtils {
public:
static String defaultDeviceValue(String prefix = "") {
#ifdef ESP32
return prefix + String((unsigned long)ESP.getEfuseMac(), HEX);
#elif defined(ESP8266)
return prefix + String(ESP.getChipId(), HEX);
#endif
}
};
#endif // end ESPUtils

View File

@ -0,0 +1,34 @@
#include <FactoryResetService.h>
using namespace std::placeholders;
FactoryResetService::FactoryResetService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : fs(fs) {
server->on(FACTORY_RESET_SERVICE_PATH,
HTTP_POST,
securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1),
AuthenticationPredicates::IS_ADMIN));
}
void FactoryResetService::handleRequest(AsyncWebServerRequest* request) {
request->onDisconnect(std::bind(&FactoryResetService::factoryReset, this));
request->send(200);
}
/**
* Delete function assumes that all files are stored flat, within the config directory
*/
void FactoryResetService::factoryReset() {
#ifdef ESP32
File root = fs->open(FS_CONFIG_DIRECTORY);
File file;
while (file = root.openNextFile()) {
fs->remove(file.name());
}
#elif defined(ESP8266)
Dir configDirectory = fs->openDir(FS_CONFIG_DIRECTORY);
while (configDirectory.next()) {
fs->remove(configDirectory.fileName());
}
#endif
ESP.restart();
}

View File

@ -0,0 +1,31 @@
#ifndef FactoryResetService_h
#define FactoryResetService_h
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include <FS.h>
#define FS_CONFIG_DIRECTORY "/config"
#define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset"
class FactoryResetService {
FS* fs;
public:
FactoryResetService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
void factoryReset();
private:
void handleRequest(AsyncWebServerRequest* request);
};
#endif // end FactoryResetService_h

View File

@ -1,3 +1,6 @@
#ifndef JsonUtils_h
#define JsonUtils_h
#include <Arduino.h>
#include <IPAddress.h>
#include <ArduinoJson.h>
@ -15,3 +18,5 @@ class JsonUtils {
}
}
};
#endif // end JsonUtils

View File

@ -22,8 +22,10 @@ class MqttConnector {
virtual void onConnect() = 0;
public:
inline AsyncMqttClient* getMqttClient() const { return _mqttClient; }
public:
inline AsyncMqttClient* getMqttClient() const {
return _mqttClient;
}
};
template <class T>

View File

@ -5,27 +5,54 @@
#include <HttpEndpoint.h>
#include <FSPersistence.h>
#include <AsyncMqttClient.h>
#include <ESPUtils.h>
#define MQTT_RECONNECTION_DELAY 5000
#define MQTT_SETTINGS_FILE "/config/mqttSettings.json"
#define MQTT_SETTINGS_SERVICE_PATH "/rest/mqttSettings"
#define MQTT_SETTINGS_SERVICE_DEFAULT_ENABLED false
#define MQTT_SETTINGS_SERVICE_DEFAULT_HOST "test.mosquitto.org"
#define MQTT_SETTINGS_SERVICE_DEFAULT_PORT 1883
#define MQTT_SETTINGS_SERVICE_DEFAULT_USERNAME ""
#define MQTT_SETTINGS_SERVICE_DEFAULT_PASSWORD ""
#define MQTT_SETTINGS_SERVICE_DEFAULT_CLIENT_ID generateClientId()
#define MQTT_SETTINGS_SERVICE_DEFAULT_KEEP_ALIVE 16
#define MQTT_SETTINGS_SERVICE_DEFAULT_CLEAN_SESSION true
#define MQTT_SETTINGS_SERVICE_DEFAULT_MAX_TOPIC_LENGTH 128
#ifndef FACTORY_MQTT_ENABLED
#define FACTORY_MQTT_ENABLED false
#endif
#ifndef FACTORY_MQTT_HOST
#define FACTORY_MQTT_HOST "test.mosquitto.org"
#endif
#ifndef FACTORY_MQTT_PORT
#define FACTORY_MQTT_PORT 1883
#endif
#ifndef FACTORY_MQTT_USERNAME
#define FACTORY_MQTT_USERNAME ""
#endif
#ifndef FACTORY_MQTT_PASSWORD
#define FACTORY_MQTT_PASSWORD ""
#endif
#ifndef FACTORY_MQTT_CLIENT_ID
#define FACTORY_MQTT_CLIENT_ID generateClientId()
#endif
#ifndef FACTORY_MQTT_KEEP_ALIVE
#define FACTORY_MQTT_KEEP_ALIVE 16
#endif
#ifndef FACTORY_MQTT_CLEAN_SESSION
#define FACTORY_MQTT_CLEAN_SESSION true
#endif
#ifndef FACTORY_MQTT_MAX_TOPIC_LENGTH
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
#endif
static String generateClientId() {
#ifdef ESP32
return "esp32-" + String((unsigned long)ESP.getEfuseMac(), HEX);
return ESPUtils::defaultDeviceValue("esp32-");
#elif defined(ESP8266)
return "esp8266-" + String(ESP.getChipId(), HEX);
return ESPUtils::defaultDeviceValue("esp8266-");
#endif
}
@ -61,15 +88,15 @@ class MqttSettings {
}
static void deserialize(JsonObject& root, MqttSettings& settings) {
settings.enabled = root["enabled"] | MQTT_SETTINGS_SERVICE_DEFAULT_ENABLED;
settings.host = root["host"] | MQTT_SETTINGS_SERVICE_DEFAULT_HOST;
settings.port = root["port"] | MQTT_SETTINGS_SERVICE_DEFAULT_PORT;
settings.username = root["username"] | MQTT_SETTINGS_SERVICE_DEFAULT_USERNAME;
settings.password = root["password"] | MQTT_SETTINGS_SERVICE_DEFAULT_PASSWORD;
settings.clientId = root["client_id"] | MQTT_SETTINGS_SERVICE_DEFAULT_CLIENT_ID;
settings.keepAlive = root["keep_alive"] | MQTT_SETTINGS_SERVICE_DEFAULT_KEEP_ALIVE;
settings.cleanSession = root["clean_session"] | MQTT_SETTINGS_SERVICE_DEFAULT_CLEAN_SESSION;
settings.maxTopicLength = root["max_topic_length"] | MQTT_SETTINGS_SERVICE_DEFAULT_MAX_TOPIC_LENGTH;
settings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED;
settings.host = root["host"] | FACTORY_MQTT_HOST;
settings.port = root["port"] | FACTORY_MQTT_PORT;
settings.username = root["username"] | FACTORY_MQTT_USERNAME;
settings.password = root["password"] | FACTORY_MQTT_PASSWORD;
settings.clientId = root["client_id"] | FACTORY_MQTT_CLIENT_ID;
settings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE;
settings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION;
settings.maxTopicLength = root["max_topic_length"] | FACTORY_MQTT_MAX_TOPIC_LENGTH;
}
};

View File

@ -11,11 +11,21 @@
#include <sntp.h>
#endif
// default time zone
#define NTP_SETTINGS_SERVICE_DEFAULT_ENABLED true
#define NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_LABEL "Europe/London"
#define NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_FORMAT "GMT0BST,M3.5.0/1,M10.5.0"
#define NTP_SETTINGS_SERVICE_DEFAULT_SERVER "time.google.com"
#ifndef FACTORY_NTP_ENABLED
#define FACTORY_NTP_ENABLED true
#endif
#ifndef FACTORY_NTP_TIME_ZONE_LABEL
#define FACTORY_NTP_TIME_ZONE_LABEL "Europe/London"
#endif
#ifndef FACTORY_NTP_TIME_ZONE_FORMAT
#define FACTORY_NTP_TIME_ZONE_FORMAT "GMT0BST,M3.5.0/1,M10.5.0"
#endif
#ifndef FACTORY_NTP_SERVER
#define FACTORY_NTP_SERVER "time.google.com"
#endif
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
@ -35,10 +45,10 @@ class NTPSettings {
}
static void deserialize(JsonObject& root, NTPSettings& settings) {
settings.enabled = root["enabled"] | NTP_SETTINGS_SERVICE_DEFAULT_ENABLED;
settings.server = root["server"] | NTP_SETTINGS_SERVICE_DEFAULT_SERVER;
settings.tzLabel = root["tz_label"] | NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_LABEL;
settings.tzFormat = root["tz_format"] | NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_FORMAT;
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
settings.server = root["server"] | FACTORY_NTP_SERVER;
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT;
}
};

View File

@ -13,10 +13,17 @@
#include <ArduinoOTA.h>
#include <WiFiUdp.h>
// Emergency defaults
#define DEFAULT_OTA_PORT 8266
#define DEFAULT_OTA_PASSWORD "esp-react"
#define DEFAULT_OTA_ENABLED true
#ifndef FACTORY_OTA_PORT
#define FACTORY_OTA_PORT 8266
#endif
#ifndef FACTORY_OTA_PASSWORD
#define FACTORY_OTA_PASSWORD "esp-react"
#endif
#ifndef FACTORY_OTA_ENABLED
#define FACTORY_OTA_ENABLED true
#endif
#define OTA_SETTINGS_FILE "/config/otaSettings.json"
#define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings"
@ -34,9 +41,9 @@ class OTASettings {
}
static void deserialize(JsonObject& root, OTASettings& settings) {
settings.enabled = root["enabled"] | DEFAULT_OTA_ENABLED;
settings.port = root["port"] | DEFAULT_OTA_PORT;
settings.password = root["password"] | DEFAULT_OTA_PASSWORD;
settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED;
settings.port = root["port"] | FACTORY_OTA_PORT;
settings.password = root["password"] | FACTORY_OTA_PASSWORD;
}
};

View File

@ -8,12 +8,6 @@ RestartService::RestartService(AsyncWebServer* server, SecurityManager* security
}
void RestartService::restart(AsyncWebServerRequest* request) {
request->onDisconnect([]() {
#ifdef ESP32
ESP.restart();
#elif defined(ESP8266)
ESP.reset();
#endif
});
request->onDisconnect([]() { ESP.restart(); });
request->send(200);
}

View File

@ -3,10 +3,13 @@
#include <ArduinoJsonJWT.h>
#include <ESPAsyncWebServer.h>
#include <ESPUtils.h>
#include <AsyncJson.h>
#include <list>
#define DEFAULT_JWT_SECRET "esp8266-react"
#ifndef FACTORY_JWT_SECRET
#define FACTORY_JWT_SECRET ESPUtils::defaultDeviceValue()
#endif
#define ACCESS_TOKEN_PARAMATER "access_token"

View File

@ -5,8 +5,21 @@
#include <HttpEndpoint.h>
#include <FSPersistence.h>
#define DEFAULT_ADMIN_USERNAME "admin"
#define DEFAULT_GUEST_USERNAME "guest"
#ifndef FACTORY_ADMIN_USERNAME
#define FACTORY_ADMIN_USERNAME "admin"
#endif
#ifndef FACTORY_ADMIN_PASSWORD
#define FACTORY_ADMIN_PASSWORD "admin"
#endif
#ifndef FACTORY_GUEST_USERNAME
#define FACTORY_GUEST_USERNAME "guest"
#endif
#ifndef FACTORY_GUEST_PASSWORD
#define FACTORY_GUEST_PASSWORD "guest"
#endif
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
#define SECURITY_SETTINGS_PATH "/rest/securitySettings"
@ -32,7 +45,7 @@ class SecuritySettings {
static void deserialize(JsonObject& root, SecuritySettings& settings) {
// secret
settings.jwtSecret = root["jwt_secret"] | DEFAULT_JWT_SECRET;
settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET;
// users
settings.users.clear();
@ -41,8 +54,8 @@ class SecuritySettings {
settings.users.push_back(User(user["username"], user["password"], user["admin"]));
}
} else {
settings.users.push_back(User(DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_USERNAME, true));
settings.users.push_back(User(DEFAULT_GUEST_USERNAME, DEFAULT_GUEST_USERNAME, false));
settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true));
settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false));
}
}
};
@ -64,7 +77,7 @@ class SecuritySettingsService : public StatefulService<SecuritySettings>, public
private:
HttpEndpoint<SecuritySettings> _httpEndpoint;
FSPersistence<SecuritySettings> _fsPersistence;
ArduinoJsonJWT _jwtHandler = ArduinoJsonJWT(DEFAULT_JWT_SECRET);
ArduinoJsonJWT _jwtHandler = ArduinoJsonJWT(FACTORY_JWT_SECRET);
void configureJWTHandler();

View File

@ -10,6 +10,18 @@
#define WIFI_SETTINGS_SERVICE_PATH "/rest/wifiSettings"
#define WIFI_RECONNECTION_DELAY 1000 * 30
#ifndef FACTORY_WIFI_SSID
#define FACTORY_WIFI_SSID ""
#endif
#ifndef FACTORY_WIFI_PASSWORD
#define FACTORY_WIFI_PASSWORD ""
#endif
#ifndef FACTORY_WIFI_HOSTNAME
#define FACTORY_WIFI_HOSTNAME ""
#endif
class WiFiSettings {
public:
// core wifi configuration
@ -41,9 +53,9 @@ class WiFiSettings {
}
static void deserialize(JsonObject& root, WiFiSettings& settings) {
settings.ssid = root["ssid"] | "";
settings.password = root["password"] | "";
settings.hostname = root["hostname"] | "";
settings.ssid = root["ssid"] | FACTORY_WIFI_SSID;
settings.password = root["password"] | FACTORY_WIFI_PASSWORD;
settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME;
settings.staticIPConfig = root["static_ip_config"] | false;
// extended settings