External config
Allow config to be accessed from outside the framework core code.
This commit is contained in:
parent
0ca9530afa
commit
39a86b0411
75
README.md
75
README.md
@ -349,10 +349,22 @@ Alternatively you can extend [AdminSettingsService.h](lib/framework/AdminSetting
|
||||
|
||||
## Extending the framework
|
||||
|
||||
It is recommend that you explore the framework code to gain a better understanding of how to use it's features. The framework provides APIs so you can add your own services or features or, if required, directly configure or observe changes to core framework features. Some of these capabilities are detailed below.
|
||||
|
||||
### Adding a service with persistant settings
|
||||
|
||||
The following code demonstrates how you might extend the framework with a feature which requires a username and password to be configured to drive an unspecified feature.
|
||||
|
||||
```cpp
|
||||
#include <SettingsService.h>
|
||||
|
||||
class ExampleSettingsService : public SettingsService {
|
||||
class ExampleSettings {
|
||||
public:
|
||||
String username;
|
||||
String password;
|
||||
};
|
||||
|
||||
class ExampleSettingsService : public SettingsService<ExampleSettings> {
|
||||
|
||||
public:
|
||||
|
||||
@ -364,20 +376,15 @@ class ExampleSettingsService : public SettingsService {
|
||||
protected:
|
||||
|
||||
void readFromJsonObject(JsonObject& root) {
|
||||
_username = root["username"] | "";
|
||||
_password = root["password"] | "";
|
||||
_settings.username = root["username"] | "";
|
||||
_settings.password = root["password"] | "";
|
||||
}
|
||||
|
||||
void writeToJsonObject(JsonObject& root) {
|
||||
root["username"] = _username;
|
||||
root["password"] = _password;
|
||||
root["username"] = _settings.username;
|
||||
root["password"] = _settings.password;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
String _username;
|
||||
String _password;
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
@ -391,7 +398,7 @@ exampleSettingsService.begin();
|
||||
|
||||
There will now be a REST service exposed on "/exampleSettings" for reading and writing (GET/POST) the settings. Any modifications will be persisted in SPIFFS, in this case to "/config/exampleSettings.json"
|
||||
|
||||
Sometimes you need to perform an action when the settings are updated, you can achieve this by overriding the onConfigUpdated() function which gets called every time the settings are updated. You can also perform an action when the service starts by overriding the begin() function, being sure to call SettingsService::begin():
|
||||
Sometimes you need to perform an action when the settings are updated, you can achieve this by overriding the onConfigUpdated() function which gets called every time the settings are updated. You can also perform an action when the service starts by overriding the begin() function, being sure to call SettingsService::begin(). You can also provide a "loop" function in order to allow your service class continuously perform an action, calling this from the main loop.
|
||||
|
||||
```cpp
|
||||
|
||||
@ -409,6 +416,50 @@ void reconfigureTheService() {
|
||||
// do whatever is required to react to the new settings
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// execute somthing as part of the main loop
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Accessing settings and services
|
||||
|
||||
The framework supplies access to it's SettingsService instances and the SecurityManager via getter functions:
|
||||
|
||||
SettingsService | Description
|
||||
---------------------------- | ----------------------------------------------
|
||||
getSecurityManager() | The security manager - detailed above
|
||||
getSecuritySettingsService() | Configures the users and other security settings
|
||||
getWiFiSettingsService() | Configures and manages the WiFi network connection
|
||||
getAPSettingsService() | Configures and manages the Access Point
|
||||
getNTPSettingsService() | Configures and manages the network time
|
||||
getOTASettingsService() | Configures and manages the Over-The-Air update feature
|
||||
|
||||
These can be used to observe changes to settings. They can also be used to fetch or update settings directly via objects, JSON strings and JsonObjects. Here are some examples of how you may use this.
|
||||
|
||||
Inspect the current WiFi settings:
|
||||
|
||||
```cpp
|
||||
WiFiSettings wifiSettings = esp8266React.getWiFiSettingsService()->fetch();
|
||||
Serial.print("The ssid is:");
|
||||
Serial.println(wifiSettings.ssid);
|
||||
```
|
||||
|
||||
Configure the SSID and password:
|
||||
|
||||
```cpp
|
||||
WiFiSettings wifiSettings = esp8266React->getWiFiSettingsService()->fetch();
|
||||
wifiSettings.ssid = "MyNetworkSSID";
|
||||
wifiSettings.password = "MySuperSecretPassword";
|
||||
esp8266React.getWiFiSettingsService()->update(wifiSettings);
|
||||
```
|
||||
|
||||
Observe changes to the WiFiSettings:
|
||||
|
||||
```cpp
|
||||
esp8266React.getWiFiSettingsService()->addUpdateHandler([]() {
|
||||
Serial.println("The WiFi Settings were updated!");
|
||||
});
|
||||
```
|
||||
|
||||
## Libraries Used
|
||||
@ -416,7 +467,5 @@ void reconfigureTheService() {
|
||||
* [React](https://reactjs.org/)
|
||||
* [Material-UI](https://material-ui-next.com/)
|
||||
* [notistack](https://github.com/iamhosseindhv/notistack)
|
||||
* [Time](https://github.com/PaulStoffregen/Time)
|
||||
* [NtpClient](https://github.com/gmag11/NtpClient)
|
||||
* [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
||||
* [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer)
|
||||
|
@ -9,7 +9,11 @@ APSettingsService::~APSettingsService() {
|
||||
|
||||
void APSettingsService::begin() {
|
||||
SettingsService::begin();
|
||||
onConfigUpdated();
|
||||
reconfigureAP();
|
||||
}
|
||||
|
||||
void APSettingsService::reconfigureAP() {
|
||||
_lastManaged = millis() - MANAGE_NETWORK_DELAY;
|
||||
}
|
||||
|
||||
void APSettingsService::loop() {
|
||||
@ -24,7 +28,8 @@ void APSettingsService::loop() {
|
||||
|
||||
void APSettingsService::manageAP() {
|
||||
WiFiMode_t currentWiFiMode = WiFi.getMode();
|
||||
if (_provisionMode == AP_MODE_ALWAYS || (_provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED)) {
|
||||
if (_settings.provisionMode == AP_MODE_ALWAYS ||
|
||||
(_settings.provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED)) {
|
||||
if (currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) {
|
||||
startAP();
|
||||
}
|
||||
@ -37,7 +42,7 @@ void APSettingsService::manageAP() {
|
||||
|
||||
void APSettingsService::startAP() {
|
||||
Serial.println("Starting software access point");
|
||||
WiFi.softAP(_ssid.c_str(), _password.c_str());
|
||||
WiFi.softAP(_settings.ssid.c_str(), _settings.password.c_str());
|
||||
if (!_dnsServer) {
|
||||
IPAddress apIp = WiFi.softAPIP();
|
||||
Serial.print("Starting captive portal on ");
|
||||
@ -65,25 +70,25 @@ void APSettingsService::handleDNS() {
|
||||
}
|
||||
|
||||
void APSettingsService::readFromJsonObject(JsonObject& root) {
|
||||
_provisionMode = root["provision_mode"] | AP_MODE_ALWAYS;
|
||||
switch (_provisionMode) {
|
||||
_settings.provisionMode = root["provision_mode"] | AP_MODE_ALWAYS;
|
||||
switch (_settings.provisionMode) {
|
||||
case AP_MODE_ALWAYS:
|
||||
case AP_MODE_DISCONNECTED:
|
||||
case AP_MODE_NEVER:
|
||||
break;
|
||||
default:
|
||||
_provisionMode = AP_MODE_ALWAYS;
|
||||
_settings.provisionMode = AP_MODE_ALWAYS;
|
||||
}
|
||||
_ssid = root["ssid"] | AP_DEFAULT_SSID;
|
||||
_password = root["password"] | AP_DEFAULT_PASSWORD;
|
||||
_settings.ssid = root["ssid"] | AP_DEFAULT_SSID;
|
||||
_settings.password = root["password"] | AP_DEFAULT_PASSWORD;
|
||||
}
|
||||
|
||||
void APSettingsService::writeToJsonObject(JsonObject& root) {
|
||||
root["provision_mode"] = _provisionMode;
|
||||
root["ssid"] = _ssid;
|
||||
root["password"] = _password;
|
||||
root["provision_mode"] = _settings.provisionMode;
|
||||
root["ssid"] = _settings.ssid;
|
||||
root["password"] = _settings.password;
|
||||
}
|
||||
|
||||
void APSettingsService::onConfigUpdated() {
|
||||
_lastManaged = millis() - MANAGE_NETWORK_DELAY;
|
||||
}
|
||||
reconfigureAP();
|
||||
}
|
||||
|
@ -19,7 +19,14 @@
|
||||
#define AP_SETTINGS_FILE "/config/apSettings.json"
|
||||
#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings"
|
||||
|
||||
class APSettingsService : public AdminSettingsService {
|
||||
class APSettings {
|
||||
public:
|
||||
uint8_t provisionMode;
|
||||
String ssid;
|
||||
String password;
|
||||
};
|
||||
|
||||
class APSettingsService : public AdminSettingsService<APSettings> {
|
||||
public:
|
||||
APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
|
||||
~APSettingsService();
|
||||
@ -33,17 +40,13 @@ class APSettingsService : public AdminSettingsService {
|
||||
void onConfigUpdated();
|
||||
|
||||
private:
|
||||
// access point settings
|
||||
uint8_t _provisionMode;
|
||||
String _ssid;
|
||||
String _password;
|
||||
|
||||
// for the mangement delay loop
|
||||
unsigned long _lastManaged;
|
||||
|
||||
// for the captive portal
|
||||
DNSServer* _dnsServer;
|
||||
|
||||
void reconfigureAP();
|
||||
void manageAP();
|
||||
void startAP();
|
||||
void stopAP();
|
||||
|
@ -3,14 +3,15 @@
|
||||
|
||||
#include <SettingsService.h>
|
||||
|
||||
class AdminSettingsService : public SettingsService {
|
||||
template <class T>
|
||||
class AdminSettingsService : public SettingsService<T> {
|
||||
public:
|
||||
AdminSettingsService(AsyncWebServer* server,
|
||||
FS* fs,
|
||||
SecurityManager* securityManager,
|
||||
char const* servicePath,
|
||||
char const* filePath) :
|
||||
SettingsService(server, fs, servicePath, filePath),
|
||||
SettingsService<T>(server, fs, servicePath, filePath),
|
||||
_securityManager(securityManager) {
|
||||
}
|
||||
|
||||
@ -26,7 +27,7 @@ class AdminSettingsService : public SettingsService {
|
||||
return;
|
||||
}
|
||||
// delegate to underlying implemetation
|
||||
SettingsService::fetchConfig(request);
|
||||
SettingsService<T>::fetchConfig(request);
|
||||
}
|
||||
|
||||
void updateConfig(AsyncWebServerRequest* request, JsonDocument& jsonDocument) {
|
||||
@ -37,7 +38,7 @@ class AdminSettingsService : public SettingsService {
|
||||
return;
|
||||
}
|
||||
// delegate to underlying implemetation
|
||||
SettingsService::updateConfig(request, jsonDocument);
|
||||
SettingsService<T>::updateConfig(request, jsonDocument);
|
||||
}
|
||||
|
||||
// override this to replace the default authentication predicate, IS_ADMIN
|
||||
|
@ -21,7 +21,7 @@ AuthenticationService::~AuthenticationService() {
|
||||
*/
|
||||
void AuthenticationService::verifyAuthorization(AsyncWebServerRequest* request) {
|
||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||
request->send(authentication.isAuthenticated() ? 200 : 401);
|
||||
request->send(authentication.authenticated ? 200 : 401);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -33,8 +33,8 @@ void AuthenticationService::signIn(AsyncWebServerRequest* request, JsonDocument&
|
||||
String username = jsonDocument["username"];
|
||||
String password = jsonDocument["password"];
|
||||
Authentication authentication = _securityManager->authenticate(username, password);
|
||||
if (authentication.isAuthenticated()) {
|
||||
User* user = authentication.getUser();
|
||||
if (authentication.authenticated) {
|
||||
User* user = authentication.user;
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE);
|
||||
JsonObject jsonObject = response->getRoot();
|
||||
jsonObject["access_token"] = _securityManager->generateJWT(user);
|
||||
|
@ -41,14 +41,34 @@ class ESP8266React {
|
||||
return &_securitySettingsService;
|
||||
}
|
||||
|
||||
SettingsService<SecuritySettings>* getSecuritySettingsService() {
|
||||
return &_securitySettingsService;
|
||||
}
|
||||
|
||||
SettingsService<WiFiSettings>* getWiFiSettingsService() {
|
||||
return &_wifiSettingsService;
|
||||
}
|
||||
|
||||
SettingsService<APSettings>* getAPSettingsService() {
|
||||
return &_apSettingsService;
|
||||
}
|
||||
|
||||
SettingsService<NTPSettings>* getNTPSettingsService() {
|
||||
return &_ntpSettingsService;
|
||||
}
|
||||
|
||||
SettingsService<OTASettings>* getOTASettingsService() {
|
||||
return &_otaSettingsService;
|
||||
}
|
||||
|
||||
private:
|
||||
SecuritySettingsService _securitySettingsService;
|
||||
|
||||
WiFiSettingsService _wifiSettingsService;
|
||||
APSettingsService _apSettingsService;
|
||||
NTPSettingsService _ntpSettingsService;
|
||||
OTASettingsService _otaSettingsService;
|
||||
RestartService _restartService;
|
||||
|
||||
AuthenticationService _authenticationService;
|
||||
|
||||
WiFiScanner _wifiScanner;
|
||||
@ -56,7 +76,6 @@ class ESP8266React {
|
||||
NTPStatus _ntpStatus;
|
||||
APStatus _apStatus;
|
||||
SystemStatus _systemStatus;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -28,17 +28,17 @@ void NTPSettingsService::loop() {
|
||||
}
|
||||
|
||||
void NTPSettingsService::readFromJsonObject(JsonObject& root) {
|
||||
_enabled = root["enabled"] | NTP_SETTINGS_SERVICE_DEFAULT_ENABLED;
|
||||
_server = root["server"] | NTP_SETTINGS_SERVICE_DEFAULT_SERVER;
|
||||
_tzLabel = root["tz_label"] | NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_LABEL;
|
||||
_tzFormat = root["tz_format"] | NTP_SETTINGS_SERVICE_DEFAULT_TIME_ZONE_FORMAT;
|
||||
_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;
|
||||
}
|
||||
|
||||
void NTPSettingsService::writeToJsonObject(JsonObject& root) {
|
||||
root["enabled"] = _enabled;
|
||||
root["server"] = _server;
|
||||
root["tz_label"] = _tzLabel;
|
||||
root["tz_format"] = _tzFormat;
|
||||
root["enabled"] = _settings.enabled;
|
||||
root["server"] = _settings.server;
|
||||
root["tz_label"] = _settings.tzLabel;
|
||||
root["tz_format"] = _settings.tzFormat;
|
||||
}
|
||||
|
||||
void NTPSettingsService::onConfigUpdated() {
|
||||
@ -47,23 +47,23 @@ void NTPSettingsService::onConfigUpdated() {
|
||||
|
||||
#ifdef ESP32
|
||||
void NTPSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
Serial.printf("Got IP address, starting NTP Synchronization\n");
|
||||
Serial.println("Got IP address, starting NTP Synchronization");
|
||||
_reconfigureNTP = true;
|
||||
}
|
||||
|
||||
void NTPSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
Serial.printf("WiFi connection dropped, stopping NTP.\n");
|
||||
Serial.println("WiFi connection dropped, stopping NTP.");
|
||||
_reconfigureNTP = false;
|
||||
sntp_stop();
|
||||
}
|
||||
#elif defined(ESP8266)
|
||||
void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) {
|
||||
Serial.printf("Got IP address, starting NTP Synchronization\n");
|
||||
Serial.println("Got IP address, starting NTP Synchronization");
|
||||
_reconfigureNTP = true;
|
||||
}
|
||||
|
||||
void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) {
|
||||
Serial.printf("WiFi connection dropped, stopping NTP.\n");
|
||||
Serial.println("WiFi connection dropped, stopping NTP.");
|
||||
_reconfigureNTP = false;
|
||||
sntp_stop();
|
||||
}
|
||||
@ -71,13 +71,13 @@ void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDis
|
||||
|
||||
void NTPSettingsService::configureNTP() {
|
||||
Serial.println("Configuring NTP...");
|
||||
if (_enabled) {
|
||||
if (_settings.enabled) {
|
||||
#ifdef ESP32
|
||||
configTzTime(_tzFormat.c_str(), _server.c_str());
|
||||
configTzTime(_settings.tzFormat.c_str(), _settings.server.c_str());
|
||||
#elif defined(ESP8266)
|
||||
configTime(_tzFormat.c_str(), _server.c_str());
|
||||
configTime(_settings.tzFormat.c_str(), _settings.server.c_str());
|
||||
#endif
|
||||
} else {
|
||||
sntp_stop();
|
||||
sntp_stop();
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,15 @@
|
||||
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
||||
#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
|
||||
|
||||
class NTPSettingsService : public AdminSettingsService {
|
||||
class NTPSettings {
|
||||
public:
|
||||
bool enabled;
|
||||
String tzLabel;
|
||||
String tzFormat;
|
||||
String server;
|
||||
};
|
||||
|
||||
class NTPSettingsService : public AdminSettingsService<NTPSettings> {
|
||||
public:
|
||||
NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
|
||||
~NTPSettingsService();
|
||||
@ -37,11 +45,6 @@ class NTPSettingsService : public AdminSettingsService {
|
||||
void receivedNTPtime();
|
||||
|
||||
private:
|
||||
bool _enabled;
|
||||
String _tzLabel;
|
||||
String _tzFormat;
|
||||
String _server;
|
||||
|
||||
bool _reconfigureNTP = false;
|
||||
|
||||
#ifdef ESP32
|
||||
|
@ -15,7 +15,7 @@ OTASettingsService::~OTASettingsService() {
|
||||
}
|
||||
|
||||
void OTASettingsService::loop() {
|
||||
if (_enabled && _arduinoOTA) {
|
||||
if ( _settings.enabled && _arduinoOTA) {
|
||||
_arduinoOTA->handle();
|
||||
}
|
||||
}
|
||||
@ -25,15 +25,15 @@ void OTASettingsService::onConfigUpdated() {
|
||||
}
|
||||
|
||||
void OTASettingsService::readFromJsonObject(JsonObject& root) {
|
||||
_enabled = root["enabled"] | DEFAULT_OTA_ENABLED;
|
||||
_port = root["port"] | DEFAULT_OTA_PORT;
|
||||
_password = root["password"] | DEFAULT_OTA_PASSWORD;
|
||||
_settings.enabled = root["enabled"] | DEFAULT_OTA_ENABLED;
|
||||
_settings.port = root["port"] | DEFAULT_OTA_PORT;
|
||||
_settings.password = root["password"] | DEFAULT_OTA_PASSWORD;
|
||||
}
|
||||
|
||||
void OTASettingsService::writeToJsonObject(JsonObject& root) {
|
||||
root["enabled"] = _enabled;
|
||||
root["port"] = _port;
|
||||
root["password"] = _password;
|
||||
root["enabled"] = _settings.enabled;
|
||||
root["port"] = _settings.port;
|
||||
root["password"] = _settings.password;
|
||||
}
|
||||
|
||||
void OTASettingsService::configureArduinoOTA() {
|
||||
@ -44,11 +44,11 @@ void OTASettingsService::configureArduinoOTA() {
|
||||
delete _arduinoOTA;
|
||||
_arduinoOTA = nullptr;
|
||||
}
|
||||
if (_enabled) {
|
||||
if (_settings.enabled) {
|
||||
Serial.println("Starting OTA Update Service");
|
||||
_arduinoOTA = new ArduinoOTAClass;
|
||||
_arduinoOTA->setPort(_port);
|
||||
_arduinoOTA->setPassword(_password.c_str());
|
||||
_arduinoOTA->setPort(_settings.port);
|
||||
_arduinoOTA->setPassword(_settings.password.c_str());
|
||||
_arduinoOTA->onStart([]() { Serial.println("Starting"); });
|
||||
_arduinoOTA->onEnd([]() { Serial.println("\nEnd"); });
|
||||
_arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
|
||||
|
@ -20,7 +20,14 @@
|
||||
#define OTA_SETTINGS_FILE "/config/otaSettings.json"
|
||||
#define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings"
|
||||
|
||||
class OTASettingsService : public AdminSettingsService {
|
||||
class OTASettings {
|
||||
public:
|
||||
bool enabled;
|
||||
int port;
|
||||
String password;
|
||||
};
|
||||
|
||||
class OTASettingsService : public AdminSettingsService<OTASettings> {
|
||||
public:
|
||||
OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
|
||||
~OTASettingsService();
|
||||
@ -34,9 +41,6 @@ class OTASettingsService : public AdminSettingsService {
|
||||
|
||||
private:
|
||||
ArduinoOTAClass* _arduinoOTA;
|
||||
bool _enabled;
|
||||
int _port;
|
||||
String _password;
|
||||
|
||||
void configureArduinoOTA();
|
||||
#ifdef ESP32
|
||||
|
@ -1,68 +0,0 @@
|
||||
#include <SecurityManager.h>
|
||||
|
||||
Authentication SecurityManager::authenticateRequest(AsyncWebServerRequest *request) {
|
||||
AsyncWebHeader *authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
|
||||
if (authorizationHeader) {
|
||||
String value = authorizationHeader->value();
|
||||
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
|
||||
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
|
||||
return authenticateJWT(value);
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
}
|
||||
|
||||
Authentication SecurityManager::authenticateJWT(String jwt) {
|
||||
DynamicJsonDocument payloadDocument(MAX_JWT_SIZE);
|
||||
_jwtHandler.parseJWT(jwt, payloadDocument);
|
||||
if (payloadDocument.is<JsonObject>()) {
|
||||
JsonObject parsedPayload = payloadDocument.as<JsonObject>();
|
||||
String username = parsedPayload["username"];
|
||||
for (User _user : _users) {
|
||||
if (_user.getUsername() == username && validatePayload(parsedPayload, &_user)) {
|
||||
return Authentication(_user);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
}
|
||||
|
||||
Authentication SecurityManager::authenticate(String username, String password) {
|
||||
for (User _user : _users) {
|
||||
if (_user.getUsername() == username && _user.getPassword() == password) {
|
||||
return Authentication(_user);
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
}
|
||||
|
||||
inline void populateJWTPayload(JsonObject &payload, User *user) {
|
||||
payload["username"] = user->getUsername();
|
||||
payload["admin"] = user->isAdmin();
|
||||
}
|
||||
|
||||
boolean SecurityManager::validatePayload(JsonObject &parsedPayload, User *user) {
|
||||
DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE);
|
||||
JsonObject payload = _jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
return payload == parsedPayload;
|
||||
}
|
||||
|
||||
String SecurityManager::generateJWT(User *user) {
|
||||
DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE);
|
||||
JsonObject payload = _jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
return _jwtHandler.buildJWT(payload);
|
||||
}
|
||||
|
||||
ArRequestHandlerFunction SecurityManager::wrapRequest(ArRequestHandlerFunction onRequest,
|
||||
AuthenticationPredicate predicate) {
|
||||
return [this, onRequest, predicate](AsyncWebServerRequest *request) {
|
||||
Authentication authentication = authenticateRequest(request);
|
||||
if (!predicate(authentication)) {
|
||||
request->send(401);
|
||||
return;
|
||||
}
|
||||
onRequest(request);
|
||||
};
|
||||
}
|
@ -14,43 +14,28 @@
|
||||
#define MAX_JWT_SIZE 128
|
||||
|
||||
class User {
|
||||
private:
|
||||
String _username;
|
||||
String _password;
|
||||
bool _admin;
|
||||
public:
|
||||
String username;
|
||||
String password;
|
||||
bool admin;
|
||||
|
||||
public:
|
||||
User(String username, String password, bool admin) : _username(username), _password(password), _admin(admin) {
|
||||
}
|
||||
String getUsername() {
|
||||
return _username;
|
||||
}
|
||||
String getPassword() {
|
||||
return _password;
|
||||
}
|
||||
bool isAdmin() {
|
||||
return _admin;
|
||||
User(String username, String password, bool admin) : username(username), password(password), admin(admin) {
|
||||
}
|
||||
};
|
||||
|
||||
class Authentication {
|
||||
private:
|
||||
User* _user;
|
||||
boolean _authenticated;
|
||||
public:
|
||||
User* user;
|
||||
boolean authenticated;
|
||||
|
||||
public:
|
||||
Authentication(User& user) : _user(new User(user)), _authenticated(true) {
|
||||
Authentication(User& user) : user(new User(user)), authenticated(true) {
|
||||
}
|
||||
Authentication() : _user(nullptr), _authenticated(false) {
|
||||
Authentication() : user(nullptr), authenticated(false) {
|
||||
}
|
||||
~Authentication() {
|
||||
delete (_user);
|
||||
}
|
||||
User* getUser() {
|
||||
return _user;
|
||||
}
|
||||
bool isAuthenticated() {
|
||||
return _authenticated;
|
||||
delete (user);
|
||||
}
|
||||
};
|
||||
|
||||
@ -62,10 +47,10 @@ class AuthenticationPredicates {
|
||||
return true;
|
||||
};
|
||||
static bool IS_AUTHENTICATED(Authentication& authentication) {
|
||||
return authentication.isAuthenticated();
|
||||
return authentication.authenticated;
|
||||
};
|
||||
static bool IS_ADMIN(Authentication& authentication) {
|
||||
return authentication.isAuthenticated() && authentication.getUser()->isAdmin();
|
||||
return authentication.authenticated && authentication.user->admin;
|
||||
};
|
||||
};
|
||||
|
||||
@ -74,37 +59,23 @@ class SecurityManager {
|
||||
/*
|
||||
* Authenticate, returning the user if found
|
||||
*/
|
||||
Authentication authenticate(String username, String password);
|
||||
virtual Authentication authenticate(String username, String password) = 0;
|
||||
|
||||
/*
|
||||
* Check the request header for the Authorization token
|
||||
*/
|
||||
Authentication authenticateRequest(AsyncWebServerRequest* request);
|
||||
virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0;
|
||||
|
||||
/*
|
||||
* Generate a JWT for the user provided
|
||||
*/
|
||||
String generateJWT(User* user);
|
||||
virtual String generateJWT(User* user) = 0;
|
||||
|
||||
/**
|
||||
* Wrap the provided request to provide validation against an AuthenticationPredicate.
|
||||
*/
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
|
||||
protected:
|
||||
ArduinoJsonJWT _jwtHandler = ArduinoJsonJWT(DEFAULT_JWT_SECRET);
|
||||
std::list<User> _users;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Lookup the user by JWT
|
||||
*/
|
||||
Authentication authenticateJWT(String jwt);
|
||||
|
||||
/*
|
||||
* Verify the payload is correct
|
||||
*/
|
||||
boolean validatePayload(JsonObject& parsedPayload, User* user);
|
||||
virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest,
|
||||
AuthenticationPredicate predicate) = 0;
|
||||
};
|
||||
|
||||
#endif // end SecurityManager_h
|
@ -12,14 +12,14 @@ void SecuritySettingsService::readFromJsonObject(JsonObject& root) {
|
||||
_jwtHandler.setSecret(root["jwt_secret"] | DEFAULT_JWT_SECRET);
|
||||
|
||||
// users
|
||||
_users.clear();
|
||||
_settings.users.clear();
|
||||
if (root["users"].is<JsonArray>()) {
|
||||
for (JsonVariant user : root["users"].as<JsonArray>()) {
|
||||
_users.push_back(User(user["username"], user["password"], user["admin"]));
|
||||
_settings.users.push_back(User(user["username"], user["password"], user["admin"]));
|
||||
}
|
||||
} else {
|
||||
_users.push_back(User(DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_USERNAME, true));
|
||||
_users.push_back(User(DEFAULT_GUEST_USERNAME, DEFAULT_GUEST_USERNAME, false));
|
||||
_settings.users.push_back(User(DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_USERNAME, true));
|
||||
_settings.users.push_back(User(DEFAULT_GUEST_USERNAME, DEFAULT_GUEST_USERNAME, false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,10 +29,78 @@ void SecuritySettingsService::writeToJsonObject(JsonObject& root) {
|
||||
|
||||
// users
|
||||
JsonArray users = root.createNestedArray("users");
|
||||
for (User _user : _users) {
|
||||
for (User _user : _settings.users) {
|
||||
JsonObject user = users.createNestedObject();
|
||||
user["username"] = _user.getUsername();
|
||||
user["password"] = _user.getPassword();
|
||||
user["admin"] = _user.isAdmin();
|
||||
user["username"] = _user.username;
|
||||
user["password"] = _user.password;
|
||||
user["admin"] = _user.admin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest *request) {
|
||||
AsyncWebHeader *authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
|
||||
if (authorizationHeader) {
|
||||
String value = authorizationHeader->value();
|
||||
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
|
||||
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
|
||||
return authenticateJWT(value);
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
}
|
||||
|
||||
Authentication SecuritySettingsService::authenticateJWT(String jwt) {
|
||||
DynamicJsonDocument payloadDocument(MAX_JWT_SIZE);
|
||||
_jwtHandler.parseJWT(jwt, payloadDocument);
|
||||
if (payloadDocument.is<JsonObject>()) {
|
||||
JsonObject parsedPayload = payloadDocument.as<JsonObject>();
|
||||
String username = parsedPayload["username"];
|
||||
for (User _user : _settings.users) {
|
||||
if (_user.username == username && validatePayload(parsedPayload, &_user)) {
|
||||
return Authentication(_user);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
}
|
||||
|
||||
Authentication SecuritySettingsService::authenticate(String username, String password) {
|
||||
for (User _user : _settings.users) {
|
||||
if (_user.username == username && _user.password == password) {
|
||||
return Authentication(_user);
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
}
|
||||
|
||||
inline void populateJWTPayload(JsonObject &payload, User *user) {
|
||||
payload["username"] = user->username;
|
||||
payload["admin"] = user->admin;
|
||||
}
|
||||
|
||||
boolean SecuritySettingsService::validatePayload(JsonObject &parsedPayload, User *user) {
|
||||
DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE);
|
||||
JsonObject payload = _jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
return payload == parsedPayload;
|
||||
}
|
||||
|
||||
String SecuritySettingsService::generateJWT(User *user) {
|
||||
DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE);
|
||||
JsonObject payload = _jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
return _jwtHandler.buildJWT(payload);
|
||||
}
|
||||
|
||||
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest,
|
||||
AuthenticationPredicate predicate) {
|
||||
return [this, onRequest, predicate](AsyncWebServerRequest *request) {
|
||||
Authentication authentication = authenticateRequest(request);
|
||||
if (!predicate(authentication)) {
|
||||
request->send(401);
|
||||
return;
|
||||
}
|
||||
onRequest(request);
|
||||
};
|
||||
}
|
||||
|
@ -10,14 +10,39 @@
|
||||
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
||||
#define SECURITY_SETTINGS_PATH "/rest/securitySettings"
|
||||
|
||||
class SecuritySettingsService : public AdminSettingsService, public SecurityManager {
|
||||
class SecuritySettings {
|
||||
public:
|
||||
String jwtSecret;
|
||||
std::list<User> users;
|
||||
};
|
||||
|
||||
class SecuritySettingsService : public AdminSettingsService<SecuritySettings>, public SecurityManager {
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer* server, FS* fs);
|
||||
~SecuritySettingsService();
|
||||
|
||||
// Functions to implement SecurityManager
|
||||
Authentication authenticate(String username, String password);
|
||||
Authentication authenticateRequest(AsyncWebServerRequest* request);
|
||||
String generateJWT(User* user);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
|
||||
protected:
|
||||
void readFromJsonObject(JsonObject& root);
|
||||
void writeToJsonObject(JsonObject& root);
|
||||
|
||||
private:
|
||||
ArduinoJsonJWT _jwtHandler = ArduinoJsonJWT(DEFAULT_JWT_SECRET);
|
||||
|
||||
/*
|
||||
* Lookup the user by JWT
|
||||
*/
|
||||
Authentication authenticateJWT(String jwt);
|
||||
|
||||
/*
|
||||
* Verify the payload is correct
|
||||
*/
|
||||
boolean validatePayload(JsonObject& parsedPayload, User* user);
|
||||
};
|
||||
|
||||
#endif // end SecuritySettingsService_h
|
@ -1,6 +1,8 @@
|
||||
#ifndef SettingsService_h
|
||||
#define SettingsService_h
|
||||
|
||||
#include <functional>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
@ -17,9 +19,24 @@
|
||||
#include <SecurityManager.h>
|
||||
#include <SettingsPersistence.h>
|
||||
|
||||
typedef size_t update_handler_id_t;
|
||||
typedef std::function<void(void)> SettingsUpdateCallback;
|
||||
static update_handler_id_t currentUpdateHandlerId;
|
||||
|
||||
typedef struct SettingsUpdateHandlerInfo {
|
||||
update_handler_id_t _id;
|
||||
SettingsUpdateCallback _cb;
|
||||
bool _allowRemove;
|
||||
SettingsUpdateHandlerInfo(SettingsUpdateCallback cb, bool allowRemove) :
|
||||
_id(++currentUpdateHandlerId),
|
||||
_cb(cb),
|
||||
_allowRemove(allowRemove){};
|
||||
} SettingsUpdateHandlerInfo_t;
|
||||
|
||||
/*
|
||||
* Abstraction of a service which stores it's settings as JSON in a file system.
|
||||
*/
|
||||
template <class T>
|
||||
class SettingsService : public SettingsPersistence {
|
||||
public:
|
||||
SettingsService(AsyncWebServer* server, FS* fs, char const* servicePath, char const* filePath) :
|
||||
@ -37,14 +54,71 @@ class SettingsService : public SettingsPersistence {
|
||||
virtual ~SettingsService() {
|
||||
}
|
||||
|
||||
update_handler_id_t addUpdateHandler(SettingsUpdateCallback cb, bool allowRemove = true) {
|
||||
if (!cb) {
|
||||
return 0;
|
||||
}
|
||||
SettingsUpdateHandlerInfo_t updateHandler(cb, allowRemove);
|
||||
_settingsUpdateHandlers.push_back(updateHandler);
|
||||
return updateHandler._id;
|
||||
}
|
||||
|
||||
void removeUpdateHandler(update_handler_id_t id) {
|
||||
for (auto i = _settingsUpdateHandlers.begin(); i != _settingsUpdateHandlers.end();) {
|
||||
if ((*i)._id == id) {
|
||||
i = _settingsUpdateHandlers.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T fetch() {
|
||||
return _settings;
|
||||
}
|
||||
|
||||
void update(T& settings) {
|
||||
_settings = settings;
|
||||
writeToFS();
|
||||
callUpdateHandlers();
|
||||
}
|
||||
|
||||
void fetchAsString(String& config) {
|
||||
DynamicJsonDocument jsonDocument(MAX_SETTINGS_SIZE);
|
||||
fetchAsDocument(jsonDocument);
|
||||
serializeJson(jsonDocument, config);
|
||||
}
|
||||
|
||||
void updateFromString(String& config) {
|
||||
DynamicJsonDocument jsonDocument(MAX_SETTINGS_SIZE);
|
||||
deserializeJson(jsonDocument, config);
|
||||
updateFromDocument(jsonDocument);
|
||||
}
|
||||
|
||||
void fetchAsDocument(JsonDocument& jsonDocument) {
|
||||
JsonObject jsonObject = jsonDocument.to<JsonObject>();
|
||||
writeToJsonObject(jsonObject);
|
||||
}
|
||||
|
||||
void updateFromDocument(JsonDocument& jsonDocument) {
|
||||
if (jsonDocument.is<JsonObject>()) {
|
||||
JsonObject newConfig = jsonDocument.as<JsonObject>();
|
||||
readFromJsonObject(newConfig);
|
||||
writeToFS();
|
||||
callUpdateHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
void begin() {
|
||||
// read the initial data from the file system
|
||||
readFromFS();
|
||||
}
|
||||
|
||||
protected:
|
||||
T _settings;
|
||||
char const* _servicePath;
|
||||
AsyncJsonWebHandler _updateHandler;
|
||||
std::list<SettingsUpdateHandlerInfo_t> _settingsUpdateHandlers;
|
||||
|
||||
virtual void fetchConfig(AsyncWebServerRequest* request) {
|
||||
// handle the request
|
||||
@ -64,7 +138,7 @@ class SettingsService : public SettingsPersistence {
|
||||
|
||||
// write settings back with a callback to reconfigure the wifi
|
||||
AsyncJsonCallbackResponse* response =
|
||||
new AsyncJsonCallbackResponse([this]() { onConfigUpdated(); }, false, MAX_SETTINGS_SIZE);
|
||||
new AsyncJsonCallbackResponse([this]() { callUpdateHandlers(); }, false, MAX_SETTINGS_SIZE);
|
||||
JsonObject jsonObject = response->getRoot();
|
||||
writeToJsonObject(jsonObject);
|
||||
response->setLength();
|
||||
@ -74,6 +148,16 @@ class SettingsService : public SettingsPersistence {
|
||||
}
|
||||
}
|
||||
|
||||
void callUpdateHandlers() {
|
||||
// call the classes own config update function
|
||||
onConfigUpdated();
|
||||
|
||||
// call all setting update handlers
|
||||
for (const SettingsUpdateHandlerInfo_t& handler : _settingsUpdateHandlers) {
|
||||
handler._cb();
|
||||
}
|
||||
}
|
||||
|
||||
// implement to perform action when config has been updated
|
||||
virtual void onConfigUpdated() {
|
||||
}
|
||||
|
@ -33,45 +33,45 @@ void WiFiSettingsService::begin() {
|
||||
}
|
||||
|
||||
void WiFiSettingsService::readFromJsonObject(JsonObject& root) {
|
||||
_ssid = root["ssid"] | "";
|
||||
_password = root["password"] | "";
|
||||
_hostname = root["hostname"] | "";
|
||||
_staticIPConfig = root["static_ip_config"] | false;
|
||||
_settings.ssid = root["ssid"] | "";
|
||||
_settings.password = root["password"] | "";
|
||||
_settings.hostname = root["hostname"] | "";
|
||||
_settings.staticIPConfig = root["static_ip_config"] | false;
|
||||
|
||||
// extended settings
|
||||
readIP(root, "local_ip", _localIP);
|
||||
readIP(root, "gateway_ip", _gatewayIP);
|
||||
readIP(root, "subnet_mask", _subnetMask);
|
||||
readIP(root, "dns_ip_1", _dnsIP1);
|
||||
readIP(root, "dns_ip_2", _dnsIP2);
|
||||
readIP(root, "local_ip", _settings.localIP);
|
||||
readIP(root, "gateway_ip", _settings.gatewayIP);
|
||||
readIP(root, "subnet_mask", _settings.subnetMask);
|
||||
readIP(root, "dns_ip_1", _settings.dnsIP1);
|
||||
readIP(root, "dns_ip_2", _settings.dnsIP2);
|
||||
|
||||
// Swap around the dns servers if 2 is populated but 1 is not
|
||||
if (_dnsIP1 == INADDR_NONE && _dnsIP2 != INADDR_NONE) {
|
||||
_dnsIP1 = _dnsIP2;
|
||||
_dnsIP2 = INADDR_NONE;
|
||||
if (_settings.dnsIP1 == INADDR_NONE && _settings.dnsIP2 != INADDR_NONE) {
|
||||
_settings.dnsIP1 = _settings.dnsIP2;
|
||||
_settings.dnsIP2 = INADDR_NONE;
|
||||
}
|
||||
|
||||
// Turning off static ip config if we don't meet the minimum requirements
|
||||
// of ipAddress, gateway and subnet. This may change to static ip only
|
||||
// as sensible defaults can be assumed for gateway and subnet
|
||||
if (_staticIPConfig && (_localIP == INADDR_NONE || _gatewayIP == INADDR_NONE || _subnetMask == INADDR_NONE)) {
|
||||
_staticIPConfig = false;
|
||||
if (_settings.staticIPConfig && (_settings.localIP == INADDR_NONE || _settings.gatewayIP == INADDR_NONE || _settings.subnetMask == INADDR_NONE)) {
|
||||
_settings.staticIPConfig = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiSettingsService::writeToJsonObject(JsonObject& root) {
|
||||
// connection settings
|
||||
root["ssid"] = _ssid;
|
||||
root["password"] = _password;
|
||||
root["hostname"] = _hostname;
|
||||
root["static_ip_config"] = _staticIPConfig;
|
||||
root["ssid"] = _settings.ssid;
|
||||
root["password"] = _settings.password;
|
||||
root["hostname"] = _settings.hostname;
|
||||
root["static_ip_config"] = _settings.staticIPConfig;
|
||||
|
||||
// extended settings
|
||||
writeIP(root, "local_ip", _localIP);
|
||||
writeIP(root, "gateway_ip", _gatewayIP);
|
||||
writeIP(root, "subnet_mask", _subnetMask);
|
||||
writeIP(root, "dns_ip_1", _dnsIP1);
|
||||
writeIP(root, "dns_ip_2", _dnsIP2);
|
||||
writeIP(root, "local_ip", _settings.localIP);
|
||||
writeIP(root, "gateway_ip", _settings.gatewayIP);
|
||||
writeIP(root, "subnet_mask", _settings.subnetMask);
|
||||
writeIP(root, "dns_ip_1", _settings.dnsIP1);
|
||||
writeIP(root, "dns_ip_2", _settings.dnsIP2);
|
||||
}
|
||||
|
||||
void WiFiSettingsService::onConfigUpdated() {
|
||||
@ -108,27 +108,27 @@ void WiFiSettingsService::loop() {
|
||||
|
||||
void WiFiSettingsService::manageSTA() {
|
||||
// Abort if already connected, or if we have no SSID
|
||||
if (WiFi.isConnected() || _ssid.length() == 0) {
|
||||
if (WiFi.isConnected() || _settings.ssid.length() == 0) {
|
||||
return;
|
||||
}
|
||||
// Connect or reconnect as required
|
||||
if ((WiFi.getMode() & WIFI_STA) == 0) {
|
||||
Serial.println("Connecting to WiFi.");
|
||||
if (_staticIPConfig) {
|
||||
if (_settings.staticIPConfig) {
|
||||
// configure for static IP
|
||||
WiFi.config(_localIP, _gatewayIP, _subnetMask, _dnsIP1, _dnsIP2);
|
||||
WiFi.config(_settings.localIP, _settings.gatewayIP, _settings.subnetMask, _settings.dnsIP1, _settings.dnsIP2);
|
||||
} else {
|
||||
// configure for DHCP
|
||||
#ifdef ESP32
|
||||
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
||||
WiFi.setHostname(_hostname.c_str());
|
||||
WiFi.setHostname(_settings.hostname.c_str());
|
||||
#elif defined(ESP8266)
|
||||
WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY);
|
||||
WiFi.hostname(_hostname);
|
||||
WiFi.hostname(_settings.hostname);
|
||||
#endif
|
||||
}
|
||||
// attempt to connect to the network
|
||||
WiFi.begin(_ssid.c_str(), _password.c_str());
|
||||
WiFi.begin(_settings.ssid.c_str(), _settings.password.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,23 @@
|
||||
#define WIFI_SETTINGS_SERVICE_PATH "/rest/wifiSettings"
|
||||
#define WIFI_RECONNECTION_DELAY 1000 * 60
|
||||
|
||||
class WiFiSettingsService : public AdminSettingsService {
|
||||
class WiFiSettings {
|
||||
public:
|
||||
// core wifi configuration
|
||||
String ssid;
|
||||
String password;
|
||||
String hostname;
|
||||
bool staticIPConfig;
|
||||
|
||||
// optional configuration for static IP address
|
||||
IPAddress localIP;
|
||||
IPAddress gatewayIP;
|
||||
IPAddress subnetMask;
|
||||
IPAddress dnsIP1;
|
||||
IPAddress dnsIP2;
|
||||
};
|
||||
|
||||
class WiFiSettingsService : public AdminSettingsService<WiFiSettings> {
|
||||
public:
|
||||
WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
|
||||
~WiFiSettingsService();
|
||||
@ -22,22 +38,8 @@ class WiFiSettingsService : public AdminSettingsService {
|
||||
void onConfigUpdated();
|
||||
|
||||
private:
|
||||
// connection settings
|
||||
String _ssid;
|
||||
String _password;
|
||||
String _hostname;
|
||||
bool _staticIPConfig;
|
||||
|
||||
// for the mangement delay loop
|
||||
unsigned long _lastConnectionAttempt;
|
||||
|
||||
// optional configuration for static IP address
|
||||
IPAddress _localIP;
|
||||
IPAddress _gatewayIP;
|
||||
IPAddress _subnetMask;
|
||||
IPAddress _dnsIP1;
|
||||
IPAddress _dnsIP2;
|
||||
|
||||
#ifdef ESP32
|
||||
void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
#elif defined(ESP8266)
|
||||
|
@ -9,7 +9,7 @@ DemoProject::~DemoProject() {
|
||||
}
|
||||
|
||||
void DemoProject::loop() {
|
||||
unsigned delay = MAX_DELAY / 255 * (255 - _blinkSpeed);
|
||||
unsigned delay = MAX_DELAY / 255 * (255 - _settings.blinkSpeed);
|
||||
unsigned long currentMillis = millis();
|
||||
if (!_lastBlink || (unsigned long)(currentMillis - _lastBlink) >= delay) {
|
||||
_lastBlink = currentMillis;
|
||||
@ -18,10 +18,10 @@ void DemoProject::loop() {
|
||||
}
|
||||
|
||||
void DemoProject::readFromJsonObject(JsonObject& root) {
|
||||
_blinkSpeed = root["blink_speed"] | DEFAULT_BLINK_SPEED;
|
||||
_settings.blinkSpeed = root["blink_speed"] | DEFAULT_BLINK_SPEED;
|
||||
}
|
||||
|
||||
void DemoProject::writeToJsonObject(JsonObject& root) {
|
||||
// connection settings
|
||||
root["blink_speed"] = _blinkSpeed;
|
||||
root["blink_speed"] = _settings.blinkSpeed;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define DemoProject_h
|
||||
|
||||
#include <AdminSettingsService.h>
|
||||
#include <ESP8266React.h>
|
||||
|
||||
#define BLINK_LED 2
|
||||
#define MAX_DELAY 1000
|
||||
@ -10,7 +11,12 @@
|
||||
#define DEMO_SETTINGS_FILE "/config/demoSettings.json"
|
||||
#define DEMO_SETTINGS_PATH "/rest/demoSettings"
|
||||
|
||||
class DemoProject : public AdminSettingsService {
|
||||
class DemoSettings {
|
||||
public:
|
||||
uint8_t blinkSpeed;
|
||||
};
|
||||
|
||||
class DemoProject : public AdminSettingsService<DemoSettings> {
|
||||
public:
|
||||
DemoProject(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
|
||||
~DemoProject();
|
||||
@ -19,7 +25,6 @@ class DemoProject : public AdminSettingsService {
|
||||
|
||||
private:
|
||||
unsigned long _lastBlink = 0;
|
||||
uint8_t _blinkSpeed = 255;
|
||||
|
||||
protected:
|
||||
void readFromJsonObject(JsonObject& root);
|
||||
|
Loading…
Reference in New Issue
Block a user