auto formatting

This commit is contained in:
proddy
2020-11-23 13:12:20 +01:00
parent 3f0c40d86c
commit d9df0bb996
39 changed files with 1521 additions and 1600 deletions

View File

@@ -1,83 +1,81 @@
#include <APSettingsService.h> #include <APSettingsService.h>
APSettingsService::APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
_httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager), : _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager)
_fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE), , _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE)
_dnsServer(nullptr), , _dnsServer(nullptr)
_lastManaged(0), , _lastManaged(0)
_reconfigureAp(false) { , _reconfigureAp(false) {
addUpdateHandler([&](const String& originId) { reconfigureAP(); }, false); addUpdateHandler([&](const String & originId) { reconfigureAP(); }, false);
} }
void APSettingsService::begin() { void APSettingsService::begin() {
_fsPersistence.readFromFS(); _fsPersistence.readFromFS();
reconfigureAP(); reconfigureAP();
} }
void APSettingsService::reconfigureAP() { void APSettingsService::reconfigureAP() {
_lastManaged = uuid::get_uptime() - MANAGE_NETWORK_DELAY; _lastManaged = uuid::get_uptime() - MANAGE_NETWORK_DELAY;
_reconfigureAp = true; _reconfigureAp = true;
} }
void APSettingsService::loop() { void APSettingsService::loop() {
unsigned long currentMillis = uuid::get_uptime(); unsigned long currentMillis = uuid::get_uptime();
unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged); unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged);
if (manageElapsed >= MANAGE_NETWORK_DELAY) { if (manageElapsed >= MANAGE_NETWORK_DELAY) {
_lastManaged = currentMillis; _lastManaged = currentMillis;
manageAP(); manageAP();
} }
handleDNS(); handleDNS();
} }
void APSettingsService::manageAP() { void APSettingsService::manageAP() {
WiFiMode_t currentWiFiMode = WiFi.getMode(); WiFiMode_t currentWiFiMode = WiFi.getMode();
if (_state.provisionMode == AP_MODE_ALWAYS || if (_state.provisionMode == AP_MODE_ALWAYS || (_state.provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED)) {
(_state.provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED)) { if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) {
if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) { startAP();
startAP(); }
} else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && (_reconfigureAp || !WiFi.softAPgetStationNum())) {
stopAP();
} }
} else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && _reconfigureAp = false;
(_reconfigureAp || !WiFi.softAPgetStationNum())) {
stopAP();
}
_reconfigureAp = false;
} }
void APSettingsService::startAP() { void APSettingsService::startAP() {
// Serial.println(F("Starting software access point")); // Serial.println(F("Starting software access point"));
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask); WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str()); WiFi.softAP(_state.ssid.c_str(), _state.password.c_str());
if (!_dnsServer) { if (!_dnsServer) {
IPAddress apIp = WiFi.softAPIP(); IPAddress apIp = WiFi.softAPIP();
// Serial.print(F("Starting captive portal on ")); // Serial.print(F("Starting captive portal on "));
// Serial.println(apIp); // Serial.println(apIp);
_dnsServer = new DNSServer; _dnsServer = new DNSServer;
_dnsServer->start(DNS_PORT, "*", apIp); _dnsServer->start(DNS_PORT, "*", apIp);
} }
} }
void APSettingsService::stopAP() { void APSettingsService::stopAP() {
if (_dnsServer) { if (_dnsServer) {
// Serial.println(F("Stopping captive portal")); // Serial.println(F("Stopping captive portal"));
_dnsServer->stop(); _dnsServer->stop();
delete _dnsServer; delete _dnsServer;
_dnsServer = nullptr; _dnsServer = nullptr;
} }
// Serial.println(F("Stopping software access point")); // Serial.println(F("Stopping software access point"));
WiFi.softAPdisconnect(true); WiFi.softAPdisconnect(true);
} }
void APSettingsService::handleDNS() { void APSettingsService::handleDNS() {
if (_dnsServer) { if (_dnsServer) {
_dnsServer->processNextRequest(); _dnsServer->processNextRequest();
} }
} }
APNetworkStatus APSettingsService::getAPNetworkStatus() { APNetworkStatus APSettingsService::getAPNetworkStatus() {
WiFiMode_t currentWiFiMode = WiFi.getMode(); WiFiMode_t currentWiFiMode = WiFi.getMode();
bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA; bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA;
if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) { if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) {
return APNetworkStatus::LINGERING; return APNetworkStatus::LINGERING;
} }
return apActive ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE; return apActive ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE;
} }

View File

@@ -48,78 +48,78 @@
enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING }; enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
class APSettings { class APSettings {
public: public:
uint8_t provisionMode; uint8_t provisionMode;
String ssid; String ssid;
String password; String password;
IPAddress localIP; IPAddress localIP;
IPAddress gatewayIP; IPAddress gatewayIP;
IPAddress subnetMask; IPAddress subnetMask;
bool operator==(const APSettings& settings) const { bool operator==(const APSettings & settings) const {
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && localIP == settings.localIP
localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask; && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask;
}
static void read(APSettings& settings, JsonObject& root) {
root["provision_mode"] = settings.provisionMode;
root["ssid"] = settings.ssid;
root["password"] = settings.password;
root["local_ip"] = settings.localIP.toString();
root["gateway_ip"] = settings.gatewayIP.toString();
root["subnet_mask"] = settings.subnetMask.toString();
}
static StateUpdateResult update(JsonObject& root, APSettings& settings) {
APSettings newSettings = {};
newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE;
switch (settings.provisionMode) {
case AP_MODE_ALWAYS:
case AP_MODE_DISCONNECTED:
case AP_MODE_NEVER:
break;
default:
newSettings.provisionMode = AP_MODE_ALWAYS;
} }
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
newSettings.password = root["password"] | FACTORY_AP_PASSWORD;
JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP); static void read(APSettings & settings, JsonObject & root) {
JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP); root["provision_mode"] = settings.provisionMode;
JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK); root["ssid"] = settings.ssid;
root["password"] = settings.password;
if (newSettings == settings) { root["local_ip"] = settings.localIP.toString();
return StateUpdateResult::UNCHANGED; root["gateway_ip"] = settings.gatewayIP.toString();
root["subnet_mask"] = settings.subnetMask.toString();
}
static StateUpdateResult update(JsonObject & root, APSettings & settings) {
APSettings newSettings = {};
newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE;
switch (settings.provisionMode) {
case AP_MODE_ALWAYS:
case AP_MODE_DISCONNECTED:
case AP_MODE_NEVER:
break;
default:
newSettings.provisionMode = AP_MODE_ALWAYS;
}
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
newSettings.password = root["password"] | FACTORY_AP_PASSWORD;
JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP);
JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP);
JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK);
if (newSettings == settings) {
return StateUpdateResult::UNCHANGED;
}
settings = newSettings;
return StateUpdateResult::CHANGED;
} }
settings = newSettings;
return StateUpdateResult::CHANGED;
}
}; };
class APSettingsService : public StatefulService<APSettings> { class APSettingsService : public StatefulService<APSettings> {
public: public:
APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void begin(); void begin();
void loop(); void loop();
APNetworkStatus getAPNetworkStatus(); APNetworkStatus getAPNetworkStatus();
private: private:
HttpEndpoint<APSettings> _httpEndpoint; HttpEndpoint<APSettings> _httpEndpoint;
FSPersistence<APSettings> _fsPersistence; FSPersistence<APSettings> _fsPersistence;
// for the captive portal // for the captive portal
DNSServer* _dnsServer; DNSServer * _dnsServer;
// for the mangement delay loop // for the mangement delay loop
volatile unsigned long _lastManaged; volatile unsigned long _lastManaged;
volatile boolean _reconfigureAp; volatile boolean _reconfigureAp;
void reconfigureAP(); void reconfigureAP();
void manageAP(); void manageAP();
void startAP(); void startAP();
void stopAP(); void stopAP();
void handleDNS(); void handleDNS();
}; };
#endif // end APSettingsConfig_h #endif // end APSettingsConfig_h

View File

@@ -1,22 +1,21 @@
#include <APStatus.h> #include <APStatus.h>
APStatus::APStatus(AsyncWebServer* server, SecurityManager* securityManager, APSettingsService* apSettingsService) : APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
_apSettingsService(apSettingsService) { : _apSettingsService(apSettingsService) {
server->on(AP_STATUS_SERVICE_PATH, server->on(AP_STATUS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, std::placeholders::_1), securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED));
AuthenticationPredicates::IS_AUTHENTICATED));
} }
void APStatus::apStatus(AsyncWebServerRequest* request) { void APStatus::apStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["status"] = _apSettingsService->getAPNetworkStatus(); root["status"] = _apSettingsService->getAPNetworkStatus();
root["ip_address"] = WiFi.softAPIP().toString(); root["ip_address"] = WiFi.softAPIP().toString();
root["mac_address"] = WiFi.softAPmacAddress(); root["mac_address"] = WiFi.softAPmacAddress();
root["station_num"] = WiFi.softAPgetStationNum(); root["station_num"] = WiFi.softAPgetStationNum();
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@@ -20,12 +20,12 @@
#define AP_STATUS_SERVICE_PATH "/rest/apStatus" #define AP_STATUS_SERVICE_PATH "/rest/apStatus"
class APStatus { class APStatus {
public: public:
APStatus(AsyncWebServer* server, SecurityManager* securityManager, APSettingsService* apSettingsService); APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService);
private: private:
APSettingsService* _apSettingsService; APSettingsService * _apSettingsService;
void apStatus(AsyncWebServerRequest* request); void apStatus(AsyncWebServerRequest * request);
}; };
#endif // end APStatus_h #endif // end APStatus_h

View File

@@ -1,14 +1,15 @@
#include "ArduinoJsonJWT.h" #include "ArduinoJsonJWT.h"
ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) { ArduinoJsonJWT::ArduinoJsonJWT(String secret)
: _secret(secret) {
} }
void ArduinoJsonJWT::setSecret(String secret) { void ArduinoJsonJWT::setSecret(String secret) {
_secret = secret; _secret = secret;
} }
String ArduinoJsonJWT::getSecret() { String ArduinoJsonJWT::getSecret() {
return _secret; return _secret;
} }
/* /*
@@ -18,127 +19,127 @@ String ArduinoJsonJWT::getSecret() {
* *
* No need to pull in additional crypto libraries - lets use what we already have. * No need to pull in additional crypto libraries - lets use what we already have.
*/ */
String ArduinoJsonJWT::sign(String& payload) { String ArduinoJsonJWT::sign(String & payload) {
unsigned char hmacResult[32]; unsigned char hmacResult[32];
{ {
#ifdef ESP32 #ifdef ESP32
mbedtls_md_context_t ctx; mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init(&ctx); mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
mbedtls_md_hmac_starts(&ctx, (unsigned char*)_secret.c_str(), _secret.length()); mbedtls_md_hmac_starts(&ctx, (unsigned char *)_secret.c_str(), _secret.length());
mbedtls_md_hmac_update(&ctx, (unsigned char*)payload.c_str(), payload.length()); mbedtls_md_hmac_update(&ctx, (unsigned char *)payload.c_str(), payload.length());
mbedtls_md_hmac_finish(&ctx, hmacResult); mbedtls_md_hmac_finish(&ctx, hmacResult);
mbedtls_md_free(&ctx); mbedtls_md_free(&ctx);
#elif defined(ESP8266) #elif defined(ESP8266)
br_hmac_key_context keyCtx; br_hmac_key_context keyCtx;
br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length()); br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length());
br_hmac_context hmacCtx; br_hmac_context hmacCtx;
br_hmac_init(&hmacCtx, &keyCtx, 0); br_hmac_init(&hmacCtx, &keyCtx, 0);
br_hmac_update(&hmacCtx, payload.c_str(), payload.length()); br_hmac_update(&hmacCtx, payload.c_str(), payload.length());
br_hmac_out(&hmacCtx, hmacResult); br_hmac_out(&hmacCtx, hmacResult);
#endif #endif
} }
return encode((char*)hmacResult, 32); return encode((char *)hmacResult, 32);
} }
String ArduinoJsonJWT::buildJWT(JsonObject& payload) { String ArduinoJsonJWT::buildJWT(JsonObject & payload) {
// serialize, then encode payload // serialize, then encode payload
String jwt; String jwt;
serializeJson(payload, jwt); serializeJson(payload, jwt);
jwt = encode(jwt.c_str(), jwt.length()); jwt = encode(jwt.c_str(), jwt.length());
// add the header to payload // add the header to payload
jwt = JWT_HEADER + '.' + jwt; jwt = JWT_HEADER + '.' + jwt;
// add signature // add signature
jwt += '.' + sign(jwt); jwt += '.' + sign(jwt);
return jwt; return jwt;
} }
void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument& jsonDocument) { void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument & jsonDocument) {
// clear json document before we begin, jsonDocument wil be null on failure // clear json document before we begin, jsonDocument wil be null on failure
jsonDocument.clear();
// must have the correct header and delimiter
if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) {
return;
}
// check there is a signature delimieter
int signatureDelimiterIndex = jwt.lastIndexOf('.');
if (signatureDelimiterIndex == JWT_HEADER_SIZE) {
return;
}
// check the signature is valid
String signature = jwt.substring(signatureDelimiterIndex + 1);
jwt = jwt.substring(0, signatureDelimiterIndex);
if (sign(jwt) != signature) {
return;
}
// decode payload
jwt = jwt.substring(JWT_HEADER_SIZE + 1);
jwt = decode(jwt);
// parse payload, clearing json document after failure
DeserializationError error = deserializeJson(jsonDocument, jwt);
if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()) {
jsonDocument.clear(); jsonDocument.clear();
}
// must have the correct header and delimiter
if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) {
return;
}
// check there is a signature delimieter
int signatureDelimiterIndex = jwt.lastIndexOf('.');
if (signatureDelimiterIndex == JWT_HEADER_SIZE) {
return;
}
// check the signature is valid
String signature = jwt.substring(signatureDelimiterIndex + 1);
jwt = jwt.substring(0, signatureDelimiterIndex);
if (sign(jwt) != signature) {
return;
}
// decode payload
jwt = jwt.substring(JWT_HEADER_SIZE + 1);
jwt = decode(jwt);
// parse payload, clearing json document after failure
DeserializationError error = deserializeJson(jsonDocument, jwt);
if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()) {
jsonDocument.clear();
}
} }
String ArduinoJsonJWT::encode(const char* cstr, int inputLen) { String ArduinoJsonJWT::encode(const char * cstr, int inputLen) {
// prepare encoder // prepare encoder
base64_encodestate _state; base64_encodestate _state;
#ifdef ESP32 #ifdef ESP32
base64_init_encodestate(&_state); base64_init_encodestate(&_state);
size_t encodedLength = base64_encode_expected_len(inputLen) + 1; size_t encodedLength = base64_encode_expected_len(inputLen) + 1;
#elif defined(ESP8266) #elif defined(ESP8266)
base64_init_encodestate_nonewlines(&_state); base64_init_encodestate_nonewlines(&_state);
size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1; size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1;
#endif #endif
// prepare buffer of correct length, returning an empty string on failure // prepare buffer of correct length, returning an empty string on failure
char* buffer = (char*)malloc(encodedLength * sizeof(char)); char * buffer = (char *)malloc(encodedLength * sizeof(char));
if (buffer == nullptr) { if (buffer == nullptr) {
return ""; return "";
} }
// encode to buffer // encode to buffer
int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state); int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state);
len += base64_encode_blockend(&buffer[len], &_state); len += base64_encode_blockend(&buffer[len], &_state);
buffer[len] = 0; buffer[len] = 0;
// convert to arduino string, freeing buffer // convert to arduino string, freeing buffer
String value = String(buffer); String value = String(buffer);
free(buffer); free(buffer);
buffer = nullptr; buffer = nullptr;
// remove padding and convert to URL safe form // remove padding and convert to URL safe form
while (value.length() > 0 && value.charAt(value.length() - 1) == '=') { while (value.length() > 0 && value.charAt(value.length() - 1) == '=') {
value.remove(value.length() - 1); value.remove(value.length() - 1);
} }
value.replace('+', '-'); value.replace('+', '-');
value.replace('/', '_'); value.replace('/', '_');
// return as string // return as string
return value; return value;
} }
String ArduinoJsonJWT::decode(String value) { String ArduinoJsonJWT::decode(String value) {
// convert to standard base64 // convert to standard base64
value.replace('-', '+'); value.replace('-', '+');
value.replace('_', '/'); value.replace('_', '/');
// prepare buffer of correct length // prepare buffer of correct length
char buffer[base64_decode_expected_len(value.length()) + 1]; char buffer[base64_decode_expected_len(value.length()) + 1];
// decode // decode
int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]); int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]);
buffer[len] = 0; buffer[len] = 0;
// return as string // return as string
return String(buffer); return String(buffer);
} }

View File

@@ -13,25 +13,25 @@
#endif #endif
class ArduinoJsonJWT { class ArduinoJsonJWT {
private: private:
String _secret; String _secret;
const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
const int JWT_HEADER_SIZE = JWT_HEADER.length(); const int JWT_HEADER_SIZE = JWT_HEADER.length();
String sign(String& value); String sign(String & value);
static String encode(const char* cstr, int len); static String encode(const char * cstr, int len);
static String decode(String value); static String decode(String value);
public: public:
ArduinoJsonJWT(String secret); ArduinoJsonJWT(String secret);
void setSecret(String secret); void setSecret(String secret);
String getSecret(); String getSecret();
String buildJWT(JsonObject& payload); String buildJWT(JsonObject & payload);
void parseJWT(String jwt, JsonDocument& jsonDocument); void parseJWT(String jwt, JsonDocument & jsonDocument);
}; };
#endif #endif

View File

@@ -2,47 +2,44 @@
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
AuthenticationService::AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager) : AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager)
_securityManager(securityManager), : _securityManager(securityManager)
_signInHandler(SIGN_IN_PATH, , _signInHandler(SIGN_IN_PATH, std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2)) {
std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2)) { server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, std::bind(&AuthenticationService::verifyAuthorization, this, std::placeholders::_1));
server->on(VERIFY_AUTHORIZATION_PATH, _signInHandler.setMethod(HTTP_POST);
HTTP_GET, _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
std::bind(&AuthenticationService::verifyAuthorization, this, std::placeholders::_1)); server->addHandler(&_signInHandler);
_signInHandler.setMethod(HTTP_POST);
_signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
server->addHandler(&_signInHandler);
} }
/** /**
* Verifys that the request supplied a valid JWT. * Verifys that the request supplied a valid JWT.
*/ */
void AuthenticationService::verifyAuthorization(AsyncWebServerRequest* request) { void AuthenticationService::verifyAuthorization(AsyncWebServerRequest * request) {
Authentication authentication = _securityManager->authenticateRequest(request); Authentication authentication = _securityManager->authenticateRequest(request);
request->send(authentication.authenticated ? 200 : 401); request->send(authentication.authenticated ? 200 : 401);
} }
/** /**
* Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in * Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in
* subsequent requests. * subsequent requests.
*/ */
void AuthenticationService::signIn(AsyncWebServerRequest* request, JsonVariant& json) { void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant & json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
String username = json["username"]; String username = json["username"];
String password = json["password"]; String password = json["password"];
Authentication authentication = _securityManager->authenticate(username, password); Authentication authentication = _securityManager->authenticate(username, password);
if (authentication.authenticated) { if (authentication.authenticated) {
User* user = authentication.user; User * user = authentication.user;
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE);
JsonObject jsonObject = response->getRoot(); JsonObject jsonObject = response->getRoot();
jsonObject["access_token"] = _securityManager->generateJWT(user); jsonObject["access_token"] = _securityManager->generateJWT(user);
response->setLength(); response->setLength();
request->send(response); request->send(response);
return; return;
}
} }
} AsyncWebServerResponse * response = request->beginResponse(401);
AsyncWebServerResponse* response = request->beginResponse(401); request->send(response);
request->send(response);
} }
#endif // end FT_ENABLED(FT_SECURITY) #endif // end FT_ENABLED(FT_SECURITY)

View File

@@ -14,17 +14,17 @@
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
class AuthenticationService { class AuthenticationService {
public: public:
AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager); AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager);
private: private:
SecurityManager* _securityManager; SecurityManager * _securityManager;
AsyncCallbackJsonWebHandler _signInHandler; AsyncCallbackJsonWebHandler _signInHandler;
// endpoint functions // endpoint functions
void signIn(AsyncWebServerRequest* request, JsonVariant& json); void signIn(AsyncWebServerRequest * request, JsonVariant & json);
void verifyAuthorization(AsyncWebServerRequest* request); void verifyAuthorization(AsyncWebServerRequest * request);
}; };
#endif // end FT_ENABLED(FT_SECURITY) #endif // end FT_ENABLED(FT_SECURITY)
#endif // end SecurityManager_h #endif // end SecurityManager_h

View File

@@ -34,84 +34,84 @@
#endif #endif
class ESP8266React { class ESP8266React {
public: public:
ESP8266React(AsyncWebServer* server, FS* fs); ESP8266React(AsyncWebServer * server, FS * fs);
void begin(); void begin();
void loop(); void loop();
SecurityManager* getSecurityManager() { SecurityManager * getSecurityManager() {
return &_securitySettingsService; return &_securitySettingsService;
} }
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
StatefulService<SecuritySettings>* getSecuritySettingsService() { StatefulService<SecuritySettings> * getSecuritySettingsService() {
return &_securitySettingsService; return &_securitySettingsService;
} }
#endif #endif
StatefulService<WiFiSettings>* getWiFiSettingsService() { StatefulService<WiFiSettings> * getWiFiSettingsService() {
return &_wifiSettingsService; return &_wifiSettingsService;
} }
StatefulService<APSettings>* getAPSettingsService() { StatefulService<APSettings> * getAPSettingsService() {
return &_apSettingsService; return &_apSettingsService;
} }
#if FT_ENABLED(FT_NTP) #if FT_ENABLED(FT_NTP)
StatefulService<NTPSettings>* getNTPSettingsService() { StatefulService<NTPSettings> * getNTPSettingsService() {
return &_ntpSettingsService; return &_ntpSettingsService;
} }
#endif #endif
#if FT_ENABLED(FT_OTA) #if FT_ENABLED(FT_OTA)
StatefulService<OTASettings>* getOTASettingsService() { StatefulService<OTASettings> * getOTASettingsService() {
return &_otaSettingsService; return &_otaSettingsService;
} }
#endif #endif
#if FT_ENABLED(FT_MQTT) #if FT_ENABLED(FT_MQTT)
StatefulService<MqttSettings>* getMqttSettingsService() { StatefulService<MqttSettings> * getMqttSettingsService() {
return &_mqttSettingsService; return &_mqttSettingsService;
} }
AsyncMqttClient* getMqttClient() { AsyncMqttClient * getMqttClient() {
return _mqttSettingsService.getMqttClient(); return _mqttSettingsService.getMqttClient();
} }
#endif #endif
void factoryReset() { void factoryReset() {
_factoryResetService.factoryReset(); _factoryResetService.factoryReset();
} }
private: private:
FeaturesService _featureService; FeaturesService _featureService;
SecuritySettingsService _securitySettingsService; SecuritySettingsService _securitySettingsService;
WiFiSettingsService _wifiSettingsService; WiFiSettingsService _wifiSettingsService;
WiFiScanner _wifiScanner; WiFiScanner _wifiScanner;
WiFiStatus _wifiStatus; WiFiStatus _wifiStatus;
APSettingsService _apSettingsService; APSettingsService _apSettingsService;
APStatus _apStatus; APStatus _apStatus;
#if FT_ENABLED(FT_NTP) #if FT_ENABLED(FT_NTP)
NTPSettingsService _ntpSettingsService; NTPSettingsService _ntpSettingsService;
NTPStatus _ntpStatus; NTPStatus _ntpStatus;
#endif #endif
#if FT_ENABLED(FT_OTA) #if FT_ENABLED(FT_OTA)
OTASettingsService _otaSettingsService; OTASettingsService _otaSettingsService;
#endif #endif
#if FT_ENABLED(FT_UPLOAD_FIRMWARE) #if FT_ENABLED(FT_UPLOAD_FIRMWARE)
UploadFirmwareService _uploadFirmwareService; UploadFirmwareService _uploadFirmwareService;
#endif #endif
#if FT_ENABLED(FT_MQTT) #if FT_ENABLED(FT_MQTT)
MqttSettingsService _mqttSettingsService; MqttSettingsService _mqttSettingsService;
MqttStatus _mqttStatus; MqttStatus _mqttStatus;
#endif #endif
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
AuthenticationService _authenticationService; AuthenticationService _authenticationService;
#endif #endif
RestartService _restartService; RestartService _restartService;
FactoryResetService _factoryResetService; FactoryResetService _factoryResetService;
SystemStatus _systemStatus; SystemStatus _systemStatus;
}; };
#endif #endif

View File

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

View File

@@ -11,7 +11,7 @@ class FSPersistence {
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService, StatefulService<T> * statefulService,
FS * fs, FS * fs,
char const * filePath, const char * filePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader) : _stateReader(stateReader)
, _stateUpdater(stateUpdater) , _stateUpdater(stateUpdater)
@@ -97,7 +97,7 @@ class FSPersistence {
JsonStateUpdater<T> _stateUpdater; JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService; StatefulService<T> * _statefulService;
FS * _fs; FS * _fs;
char const * _filePath; const char * _filePath;
size_t _bufferSize; size_t _bufferSize;
update_handler_id_t _updateHandlerId; update_handler_id_t _updateHandlerId;

View File

@@ -18,15 +18,15 @@
#define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset" #define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset"
class FactoryResetService { class FactoryResetService {
FS* fs; FS * fs;
public: public:
FactoryResetService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void factoryReset(); void factoryReset();
private: private:
void handleRequest(AsyncWebServerRequest* request); void handleRequest(AsyncWebServerRequest * request);
}; };
#endif // end FactoryResetService_h #endif // end FactoryResetService_h

View File

@@ -1,42 +1,42 @@
#include <FeaturesService.h> #include <FeaturesService.h>
FeaturesService::FeaturesService(AsyncWebServer* server) { FeaturesService::FeaturesService(AsyncWebServer * server) {
server->on(FEATURES_SERVICE_PATH, HTTP_GET, std::bind(&FeaturesService::features, this, std::placeholders::_1)); server->on(FEATURES_SERVICE_PATH, HTTP_GET, std::bind(&FeaturesService::features, this, std::placeholders::_1));
} }
void FeaturesService::features(AsyncWebServerRequest* request) { void FeaturesService::features(AsyncWebServerRequest * request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
#if FT_ENABLED(FT_PROJECT) #if FT_ENABLED(FT_PROJECT)
root["project"] = true; root["project"] = true;
#else #else
root["project"] = false; root["project"] = false;
#endif #endif
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
root["security"] = true; root["security"] = true;
#else #else
root["security"] = false; root["security"] = false;
#endif #endif
#if FT_ENABLED(FT_MQTT) #if FT_ENABLED(FT_MQTT)
root["mqtt"] = true; root["mqtt"] = true;
#else #else
root["mqtt"] = false; root["mqtt"] = false;
#endif #endif
#if FT_ENABLED(FT_NTP) #if FT_ENABLED(FT_NTP)
root["ntp"] = true; root["ntp"] = true;
#else #else
root["ntp"] = false; root["ntp"] = false;
#endif #endif
#if FT_ENABLED(FT_OTA) #if FT_ENABLED(FT_OTA)
root["ota"] = true; root["ota"] = true;
#else #else
root["ota"] = false; root["ota"] = false;
#endif #endif
#if FT_ENABLED(FT_UPLOAD_FIRMWARE) #if FT_ENABLED(FT_UPLOAD_FIRMWARE)
root["upload_firmware"] = true; root["upload_firmware"] = true;
#else #else
root["upload_firmware"] = false; root["upload_firmware"] = false;
#endif #endif
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@@ -19,11 +19,11 @@
#define FEATURES_SERVICE_PATH "/rest/features" #define FEATURES_SERVICE_PATH "/rest/features"
class FeaturesService { class FeaturesService {
public: public:
FeaturesService(AsyncWebServer* server); FeaturesService(AsyncWebServer * server);
private: private:
void features(AsyncWebServerRequest* request); void features(AsyncWebServerRequest * request);
}; };
#endif #endif

View File

@@ -13,153 +13,139 @@
template <class T> template <class T>
class HttpGetEndpoint { class HttpGetEndpoint {
public: public:
HttpGetEndpoint(JsonStateReader<T> stateReader, HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
const String& servicePath, const String & servicePath,
SecurityManager* securityManager, SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { : _stateReader(stateReader)
server->on(servicePath.c_str(), , _statefulService(statefulService)
HTTP_GET, , _bufferSize(bufferSize) {
securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1), server->on(servicePath.c_str(),
authenticationPredicate)); HTTP_GET,
} securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1), authenticationPredicate));
}
HttpGetEndpoint(JsonStateReader<T> stateReader, HttpGetEndpoint(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
const String& servicePath, const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { : _stateReader(stateReader)
server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1)); , _statefulService(statefulService)
} , _bufferSize(bufferSize) {
server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1));
}
protected: protected:
JsonStateReader<T> _stateReader; JsonStateReader<T> _stateReader;
StatefulService<T>* _statefulService; StatefulService<T> * _statefulService;
size_t _bufferSize; size_t _bufferSize;
void fetchSettings(AsyncWebServerRequest* request) { void fetchSettings(AsyncWebServerRequest * request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize); AsyncJsonResponse * response = new AsyncJsonResponse(false, _bufferSize);
JsonObject jsonObject = response->getRoot().to<JsonObject>(); JsonObject jsonObject = response->getRoot().to<JsonObject>();
_statefulService->read(jsonObject, _stateReader); _statefulService->read(jsonObject, _stateReader);
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }
}; };
template <class T> template <class T>
class HttpPostEndpoint { class HttpPostEndpoint {
public: public:
HttpPostEndpoint(JsonStateReader<T> stateReader, HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
const String& servicePath, const String & servicePath,
SecurityManager* securityManager, SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
_stateReader(stateReader), : _stateReader(stateReader)
_stateUpdater(stateUpdater), , _stateUpdater(stateUpdater)
_statefulService(statefulService), , _statefulService(statefulService)
_updateHandler( , _updateHandler(servicePath,
servicePath, securityManager->wrapCallback(std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
securityManager->wrapCallback( authenticationPredicate),
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), bufferSize)
authenticationPredicate), , _bufferSize(bufferSize) {
bufferSize), _updateHandler.setMethod(HTTP_POST);
_bufferSize(bufferSize) { server->addHandler(&_updateHandler);
_updateHandler.setMethod(HTTP_POST);
server->addHandler(&_updateHandler);
}
HttpPostEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader),
_stateUpdater(stateUpdater),
_statefulService(statefulService),
_updateHandler(servicePath,
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
bufferSize),
_bufferSize(bufferSize) {
_updateHandler.setMethod(HTTP_POST);
server->addHandler(&_updateHandler);
}
protected:
JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater;
StatefulService<T>* _statefulService;
AsyncCallbackJsonWebHandler _updateHandler;
size_t _bufferSize;
void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) {
if (!json.is<JsonObject>()) {
request->send(400);
return;
} }
JsonObject jsonObject = json.as<JsonObject>();
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); HttpPostEndpoint(JsonStateReader<T> stateReader,
if (outcome == StateUpdateResult::ERROR) { JsonStateUpdater<T> stateUpdater,
request->send(400); StatefulService<T> * statefulService,
return; AsyncWebServer * server,
const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader)
, _stateUpdater(stateUpdater)
, _statefulService(statefulService)
, _updateHandler(servicePath, std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), bufferSize)
, _bufferSize(bufferSize) {
_updateHandler.setMethod(HTTP_POST);
server->addHandler(&_updateHandler);
} }
if (outcome == StateUpdateResult::CHANGED) {
request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); }); protected:
JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService;
AsyncCallbackJsonWebHandler _updateHandler;
size_t _bufferSize;
void updateSettings(AsyncWebServerRequest * request, JsonVariant & json) {
if (!json.is<JsonObject>()) {
request->send(400);
return;
}
JsonObject jsonObject = json.as<JsonObject>();
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
if (outcome == StateUpdateResult::ERROR) {
request->send(400);
return;
}
if (outcome == StateUpdateResult::CHANGED) {
request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); });
}
AsyncJsonResponse * response = new AsyncJsonResponse(false, _bufferSize);
jsonObject = response->getRoot().to<JsonObject>();
_statefulService->read(jsonObject, _stateReader);
response->setLength();
request->send(response);
} }
AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize);
jsonObject = response->getRoot().to<JsonObject>();
_statefulService->read(jsonObject, _stateReader);
response->setLength();
request->send(response);
}
}; };
template <class T> template <class T>
class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> { class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
public: public:
HttpEndpoint(JsonStateReader<T> stateReader, HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
const String& servicePath, const String & servicePath,
SecurityManager* securityManager, SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
HttpGetEndpoint<T>(stateReader, : HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, securityManager, authenticationPredicate, bufferSize)
statefulService, , HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, securityManager, authenticationPredicate, bufferSize) {
server, }
servicePath,
securityManager,
authenticationPredicate,
bufferSize),
HttpPostEndpoint<T>(stateReader,
stateUpdater,
statefulService,
server,
servicePath,
securityManager,
authenticationPredicate,
bufferSize) {
}
HttpEndpoint(JsonStateReader<T> stateReader, HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
const String& servicePath, const String & servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize), : HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize)
HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) { , HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) {
} }
}; };
#endif // end HttpEndpoint #endif // end HttpEndpoint

View File

@@ -6,24 +6,24 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
class JsonUtils { class JsonUtils {
public: public:
static void readIP(JsonObject& root, const String& key, IPAddress& ip, const String& def) { static void readIP(JsonObject & root, const String & key, IPAddress & ip, const String & def) {
IPAddress defaultIp = {}; IPAddress defaultIp = {};
if (!defaultIp.fromString(def)) { if (!defaultIp.fromString(def)) {
defaultIp = INADDR_NONE; defaultIp = INADDR_NONE;
}
readIP(root, key, ip, defaultIp);
} }
readIP(root, key, ip, defaultIp); static void readIP(JsonObject & root, const String & key, IPAddress & ip, const IPAddress & defaultIp = INADDR_NONE) {
} if (!root[key].is<String>() || !ip.fromString(root[key].as<String>())) {
static void readIP(JsonObject& root, const String& key, IPAddress& ip, const IPAddress& defaultIp = INADDR_NONE) { ip = defaultIp;
if (!root[key].is<String>() || !ip.fromString(root[key].as<String>())) { }
ip = defaultIp;
} }
} static void writeIP(JsonObject & root, const String & key, const IPAddress & ip) {
static void writeIP(JsonObject& root, const String& key, const IPAddress& ip) { if (ip != INADDR_NONE) {
if (ip != INADDR_NONE) { root[key] = ip.toString();
root[key] = ip.toString(); }
} }
}
}; };
#endif // end JsonUtils #endif // end JsonUtils

View File

@@ -8,160 +8,161 @@
template <class T> template <class T>
class MqttConnector { class MqttConnector {
protected: protected:
StatefulService<T>* _statefulService; StatefulService<T> * _statefulService;
AsyncMqttClient* _mqttClient; AsyncMqttClient * _mqttClient;
size_t _bufferSize; size_t _bufferSize;
MqttConnector(StatefulService<T>* statefulService, AsyncMqttClient* mqttClient, size_t bufferSize) : MqttConnector(StatefulService<T> * statefulService, AsyncMqttClient * mqttClient, size_t bufferSize)
_statefulService(statefulService), _mqttClient(mqttClient), _bufferSize(bufferSize) { : _statefulService(statefulService)
_mqttClient->onConnect(std::bind(&MqttConnector::onConnect, this)); , _mqttClient(mqttClient)
} , _bufferSize(bufferSize) {
_mqttClient->onConnect(std::bind(&MqttConnector::onConnect, this));
}
virtual void onConnect() = 0; virtual void onConnect() = 0;
public: public:
inline AsyncMqttClient* getMqttClient() const { inline AsyncMqttClient * getMqttClient() const {
return _mqttClient; return _mqttClient;
} }
}; };
template <class T> template <class T>
class MqttPub : virtual public MqttConnector<T> { class MqttPub : virtual public MqttConnector<T> {
public: public:
MqttPub(JsonStateReader<T> stateReader, MqttPub(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncMqttClient* mqttClient, AsyncMqttClient * mqttClient,
const String& pubTopic = "", const String & pubTopic = "",
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
MqttConnector<T>(statefulService, mqttClient, bufferSize), _stateReader(stateReader), _pubTopic(pubTopic) { : MqttConnector<T>(statefulService, mqttClient, bufferSize)
MqttConnector<T>::_statefulService->addUpdateHandler([&](const String& originId) { publish(); }, false); , _stateReader(stateReader)
} , _pubTopic(pubTopic) {
MqttConnector<T>::_statefulService->addUpdateHandler([&](const String & originId) { publish(); }, false);
void setPubTopic(const String& pubTopic) { }
_pubTopic = pubTopic;
publish(); void setPubTopic(const String & pubTopic) {
} _pubTopic = pubTopic;
publish();
protected: }
virtual void onConnect() {
publish(); protected:
} virtual void onConnect() {
publish();
private: }
JsonStateReader<T> _stateReader;
String _pubTopic; private:
JsonStateReader<T> _stateReader;
void publish() { String _pubTopic;
if (_pubTopic.length() > 0 && MqttConnector<T>::_mqttClient->connected()) {
// serialize to json doc void publish() {
DynamicJsonDocument json(MqttConnector<T>::_bufferSize); if (_pubTopic.length() > 0 && MqttConnector<T>::_mqttClient->connected()) {
JsonObject jsonObject = json.to<JsonObject>(); // serialize to json doc
MqttConnector<T>::_statefulService->read(jsonObject, _stateReader); DynamicJsonDocument json(MqttConnector<T>::_bufferSize);
JsonObject jsonObject = json.to<JsonObject>();
// serialize to string MqttConnector<T>::_statefulService->read(jsonObject, _stateReader);
String payload;
serializeJson(json, payload); // serialize to string
String payload;
// publish the payload serializeJson(json, payload);
MqttConnector<T>::_mqttClient->publish(_pubTopic.c_str(), 0, false, payload.c_str());
// publish the payload
MqttConnector<T>::_mqttClient->publish(_pubTopic.c_str(), 0, false, payload.c_str());
}
} }
}
}; };
template <class T> template <class T>
class MqttSub : virtual public MqttConnector<T> { class MqttSub : virtual public MqttConnector<T> {
public: public:
MqttSub(JsonStateUpdater<T> stateUpdater, MqttSub(JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncMqttClient* mqttClient, AsyncMqttClient * mqttClient,
const String& subTopic = "", const String & subTopic = "",
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
MqttConnector<T>(statefulService, mqttClient, bufferSize), _stateUpdater(stateUpdater), _subTopic(subTopic) { : MqttConnector<T>(statefulService, mqttClient, bufferSize)
MqttConnector<T>::_mqttClient->onMessage(std::bind(&MqttSub::onMqttMessage, , _stateUpdater(stateUpdater)
this, , _subTopic(subTopic) {
std::placeholders::_1, MqttConnector<T>::_mqttClient->onMessage(std::bind(&MqttSub::onMqttMessage,
std::placeholders::_2, this,
std::placeholders::_3, std::placeholders::_1,
std::placeholders::_4, std::placeholders::_2,
std::placeholders::_5, std::placeholders::_3,
std::placeholders::_6)); std::placeholders::_4,
} std::placeholders::_5,
std::placeholders::_6));
void setSubTopic(const String& subTopic) {
if (!_subTopic.equals(subTopic)) {
// unsubscribe from the existing topic if one was set
if (_subTopic.length() > 0) {
MqttConnector<T>::_mqttClient->unsubscribe(_subTopic.c_str());
}
// set the new topic and re-configure the subscription
_subTopic = subTopic;
subscribe();
}
}
protected:
virtual void onConnect() {
subscribe();
}
private:
JsonStateUpdater<T> _stateUpdater;
String _subTopic;
void subscribe() {
if (_subTopic.length() > 0) {
MqttConnector<T>::_mqttClient->subscribe(_subTopic.c_str(), 2);
}
}
void onMqttMessage(char* topic,
char* payload,
AsyncMqttClientMessageProperties properties,
size_t len,
size_t index,
size_t total) {
// we only care about the topic we are watching in this class
if (strcmp(_subTopic.c_str(), topic)) {
return;
} }
// deserialize from string void setSubTopic(const String & subTopic) {
DynamicJsonDocument json(MqttConnector<T>::_bufferSize); if (!_subTopic.equals(subTopic)) {
DeserializationError error = deserializeJson(json, payload, len); // unsubscribe from the existing topic if one was set
if (!error && json.is<JsonObject>()) { if (_subTopic.length() > 0) {
JsonObject jsonObject = json.as<JsonObject>(); MqttConnector<T>::_mqttClient->unsubscribe(_subTopic.c_str());
MqttConnector<T>::_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID); }
// set the new topic and re-configure the subscription
_subTopic = subTopic;
subscribe();
}
}
protected:
virtual void onConnect() {
subscribe();
}
private:
JsonStateUpdater<T> _stateUpdater;
String _subTopic;
void subscribe() {
if (_subTopic.length() > 0) {
MqttConnector<T>::_mqttClient->subscribe(_subTopic.c_str(), 2);
}
}
void onMqttMessage(char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
// we only care about the topic we are watching in this class
if (strcmp(_subTopic.c_str(), topic)) {
return;
}
// deserialize from string
DynamicJsonDocument json(MqttConnector<T>::_bufferSize);
DeserializationError error = deserializeJson(json, payload, len);
if (!error && json.is<JsonObject>()) {
JsonObject jsonObject = json.as<JsonObject>();
MqttConnector<T>::_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID);
}
} }
}
}; };
template <class T> template <class T>
class MqttPubSub : public MqttPub<T>, public MqttSub<T> { class MqttPubSub : public MqttPub<T>, public MqttSub<T> {
public: public:
MqttPubSub(JsonStateReader<T> stateReader, MqttPubSub(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncMqttClient* mqttClient, AsyncMqttClient * mqttClient,
const String& pubTopic = "", const String & pubTopic = "",
const String& subTopic = "", const String & subTopic = "",
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
MqttConnector<T>(statefulService, mqttClient, bufferSize), : MqttConnector<T>(statefulService, mqttClient, bufferSize)
MqttPub<T>(stateReader, statefulService, mqttClient, pubTopic, bufferSize), , MqttPub<T>(stateReader, statefulService, mqttClient, pubTopic, bufferSize)
MqttSub<T>(stateUpdater, statefulService, mqttClient, subTopic, bufferSize) { , MqttSub<T>(stateUpdater, statefulService, mqttClient, subTopic, bufferSize) {
} }
public: public:
void configureTopics(const String& pubTopic, const String& subTopic) { void configureTopics(const String & pubTopic, const String & subTopic) {
MqttSub<T>::setSubTopic(subTopic); MqttSub<T>::setSubTopic(subTopic);
MqttPub<T>::setPubTopic(pubTopic); MqttPub<T>::setPubTopic(pubTopic);
} }
protected: protected:
void onConnect() { void onConnect() {
MqttSub<T>::onConnect(); MqttSub<T>::onConnect();
MqttPub<T>::onConnect(); MqttPub<T>::onConnect();
} }
}; };
#endif // end MqttPubSub #endif // end MqttPubSub

View File

@@ -1,90 +1,85 @@
#include <NTPSettingsService.h> #include <NTPSettingsService.h>
NTPSettingsService::NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
_httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager), : _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager)
_fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE), , _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE)
_timeHandler(TIME_PATH, , _timeHandler(TIME_PATH,
securityManager->wrapCallback( securityManager->wrapCallback(std::bind(&NTPSettingsService::configureTime, this, std::placeholders::_1, std::placeholders::_2),
std::bind(&NTPSettingsService::configureTime, this, std::placeholders::_1, std::placeholders::_2), AuthenticationPredicates::IS_ADMIN)) {
AuthenticationPredicates::IS_ADMIN)) { _timeHandler.setMethod(HTTP_POST);
_timeHandler.setMethod(HTTP_POST); _timeHandler.setMaxContentLength(MAX_TIME_SIZE);
_timeHandler.setMaxContentLength(MAX_TIME_SIZE); server->addHandler(&_timeHandler);
server->addHandler(&_timeHandler);
#ifdef ESP32 #ifdef ESP32
WiFi.onEvent( WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2),
std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2),
WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
#elif defined(ESP8266) #elif defined(ESP8266)
_onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1));
std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); _onStationModeGotIPHandler = WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1));
_onStationModeGotIPHandler =
WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1));
#endif #endif
addUpdateHandler([&](const String& originId) { configureNTP(); }, false); addUpdateHandler([&](const String & originId) { configureNTP(); }, false);
} }
void NTPSettingsService::begin() { void NTPSettingsService::begin() {
_fsPersistence.readFromFS(); _fsPersistence.readFromFS();
configureNTP(); configureNTP();
} }
#ifdef ESP32 #ifdef ESP32
void NTPSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { void NTPSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
// Serial.println(F("Got IP address, starting NTP Synchronization")); // Serial.println(F("Got IP address, starting NTP Synchronization"));
configureNTP(); configureNTP();
} }
void NTPSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { void NTPSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
// Serial.println(F("WiFi connection dropped, stopping NTP.")); // Serial.println(F("WiFi connection dropped, stopping NTP."));
configureNTP(); configureNTP();
} }
#elif defined(ESP8266) #elif defined(ESP8266)
void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP & event) {
// Serial.println(F("Got IP address, starting NTP Synchronization")); // Serial.println(F("Got IP address, starting NTP Synchronization"));
configureNTP(); configureNTP();
} }
void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected & event) {
// Serial.println(F("WiFi connection dropped, stopping NTP.")); // Serial.println(F("WiFi connection dropped, stopping NTP."));
configureNTP(); configureNTP();
} }
#endif #endif
void NTPSettingsService::configureNTP() { void NTPSettingsService::configureNTP() {
if (WiFi.isConnected() && _state.enabled) { if (WiFi.isConnected() && _state.enabled) {
// Serial.println(F("Starting NTP...")); // Serial.println(F("Starting NTP..."));
#ifdef ESP32 #ifdef ESP32
configTzTime(_state.tzFormat.c_str(), _state.server.c_str()); configTzTime(_state.tzFormat.c_str(), _state.server.c_str());
#elif defined(ESP8266) #elif defined(ESP8266)
configTime(_state.tzFormat.c_str(), _state.server.c_str()); configTime(_state.tzFormat.c_str(), _state.server.c_str());
#endif #endif
} else { } else {
#ifdef ESP32 #ifdef ESP32
setenv("TZ", _state.tzFormat.c_str(), 1); setenv("TZ", _state.tzFormat.c_str(), 1);
tzset(); tzset();
#elif defined(ESP8266) #elif defined(ESP8266)
setTZ(_state.tzFormat.c_str()); setTZ(_state.tzFormat.c_str());
#endif #endif
sntp_stop(); sntp_stop();
} }
} }
void NTPSettingsService::configureTime(AsyncWebServerRequest* request, JsonVariant& json) { void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant & json) {
if (!sntp_enabled() && json.is<JsonObject>()) { if (!sntp_enabled() && json.is<JsonObject>()) {
String timeUtc = json["time_utc"]; String timeUtc = json["time_utc"];
struct tm tm = {0}; struct tm tm = {0};
char* s = strptime(timeUtc.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm); char * s = strptime(timeUtc.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm);
if (s != nullptr) { if (s != nullptr) {
time_t time = mktime(&tm); time_t time = mktime(&tm);
struct timeval now = {.tv_sec = time}; struct timeval now = {.tv_sec = time};
settimeofday(&now, nullptr); settimeofday(&now, nullptr);
AsyncWebServerResponse* response = request->beginResponse(200); AsyncWebServerResponse * response = request->beginResponse(200);
request->send(response); request->send(response);
return; return;
}
} }
} AsyncWebServerResponse * response = request->beginResponse(400);
AsyncWebServerResponse* response = request->beginResponse(400); request->send(response);
request->send(response);
} }

View File

@@ -34,51 +34,51 @@
#define TIME_PATH "/rest/time" #define TIME_PATH "/rest/time"
class NTPSettings { class NTPSettings {
public: public:
bool enabled; bool enabled;
String tzLabel; String tzLabel;
String tzFormat; String tzFormat;
String server; String server;
static void read(NTPSettings& settings, JsonObject& root) { static void read(NTPSettings & settings, JsonObject & root) {
root["enabled"] = settings.enabled; root["enabled"] = settings.enabled;
root["server"] = settings.server; root["server"] = settings.server;
root["tz_label"] = settings.tzLabel; root["tz_label"] = settings.tzLabel;
root["tz_format"] = settings.tzFormat; root["tz_format"] = settings.tzFormat;
} }
static StateUpdateResult update(JsonObject& root, NTPSettings& settings) { static StateUpdateResult update(JsonObject & root, NTPSettings & settings) {
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED; settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
settings.server = root["server"] | FACTORY_NTP_SERVER; settings.server = root["server"] | FACTORY_NTP_SERVER;
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL; settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT; settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT;
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
} }
}; };
class NTPSettingsService : public StatefulService<NTPSettings> { class NTPSettingsService : public StatefulService<NTPSettings> {
public: public:
NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void begin(); void begin();
private: private:
HttpEndpoint<NTPSettings> _httpEndpoint; HttpEndpoint<NTPSettings> _httpEndpoint;
FSPersistence<NTPSettings> _fsPersistence; FSPersistence<NTPSettings> _fsPersistence;
AsyncCallbackJsonWebHandler _timeHandler; AsyncCallbackJsonWebHandler _timeHandler;
#ifdef ESP32 #ifdef ESP32
void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
#elif defined(ESP8266) #elif defined(ESP8266)
WiFiEventHandler _onStationModeDisconnectedHandler; WiFiEventHandler _onStationModeDisconnectedHandler;
WiFiEventHandler _onStationModeGotIPHandler; WiFiEventHandler _onStationModeGotIPHandler;
void onStationModeGotIP(const WiFiEventStationModeGotIP& event); void onStationModeGotIP(const WiFiEventStationModeGotIP & event);
void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); void onStationModeDisconnected(const WiFiEventStationModeDisconnected & event);
#endif #endif
void configureNTP(); void configureNTP();
void configureTime(AsyncWebServerRequest* request, JsonVariant& json); void configureTime(AsyncWebServerRequest * request, JsonVariant & json);
}; };
#endif // end NTPSettingsService_h #endif // end NTPSettingsService_h

View File

@@ -1,40 +1,39 @@
#include <NTPStatus.h> #include <NTPStatus.h>
NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) { NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(NTP_STATUS_SERVICE_PATH, server->on(NTP_STATUS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED));
AuthenticationPredicates::IS_AUTHENTICATED));
} }
String toISOString(tm* time, bool incOffset) { String toISOString(tm * time, bool incOffset) {
char time_string[25]; char time_string[25];
strftime(time_string, 25, incOffset ? "%FT%T%z" : "%FT%TZ", time); strftime(time_string, 25, incOffset ? "%FT%T%z" : "%FT%TZ", time);
return String(time_string); return String(time_string);
} }
void NTPStatus::ntpStatus(AsyncWebServerRequest* request) { void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
// grab the current instant in unix seconds // grab the current instant in unix seconds
time_t now = time(nullptr); time_t now = time(nullptr);
// only provide enabled/disabled status for now // only provide enabled/disabled status for now
root["status"] = sntp_enabled() ? 1 : 0; root["status"] = sntp_enabled() ? 1 : 0;
// the current time in UTC // the current time in UTC
root["time_utc"] = toISOString(gmtime(&now), false); root["time_utc"] = toISOString(gmtime(&now), false);
// local time as ISO String with TZ // local time as ISO String with TZ
root["time_local"] = toISOString(localtime(&now), true); root["time_local"] = toISOString(localtime(&now), true);
// the sntp server name // the sntp server name
root["server"] = sntp_getservername(0); root["server"] = sntp_getservername(0);
// device uptime in seconds // device uptime in seconds
root["uptime"] = uuid::get_uptime() / 1000; root["uptime"] = uuid::get_uptime() / 1000;
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@@ -23,11 +23,11 @@
#define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus" #define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus"
class NTPStatus { class NTPStatus {
public: public:
NTPStatus(AsyncWebServer* server, SecurityManager* securityManager); NTPStatus(AsyncWebServer * server, SecurityManager * securityManager);
private: private:
void ntpStatus(AsyncWebServerRequest* request); void ntpStatus(AsyncWebServerRequest * request);
}; };
#endif // end NTPStatus_h #endif // end NTPStatus_h

View File

@@ -31,44 +31,44 @@
#define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings" #define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings"
class OTASettings { class OTASettings {
public: public:
bool enabled; bool enabled;
int port; int port;
String password; String password;
static void read(OTASettings& settings, JsonObject& root) { static void read(OTASettings & settings, JsonObject & root) {
root["enabled"] = settings.enabled; root["enabled"] = settings.enabled;
root["port"] = settings.port; root["port"] = settings.port;
root["password"] = settings.password; root["password"] = settings.password;
} }
static StateUpdateResult update(JsonObject& root, OTASettings& settings) { static StateUpdateResult update(JsonObject & root, OTASettings & settings) {
settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED; settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED;
settings.port = root["port"] | FACTORY_OTA_PORT; settings.port = root["port"] | FACTORY_OTA_PORT;
settings.password = root["password"] | FACTORY_OTA_PASSWORD; settings.password = root["password"] | FACTORY_OTA_PASSWORD;
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
} }
}; };
class OTASettingsService : public StatefulService<OTASettings> { class OTASettingsService : public StatefulService<OTASettings> {
public: public:
OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void begin(); void begin();
void loop(); void loop();
private: private:
HttpEndpoint<OTASettings> _httpEndpoint; HttpEndpoint<OTASettings> _httpEndpoint;
FSPersistence<OTASettings> _fsPersistence; FSPersistence<OTASettings> _fsPersistence;
ArduinoOTAClass* _arduinoOTA; ArduinoOTAClass * _arduinoOTA;
void configureArduinoOTA(); void configureArduinoOTA();
#ifdef ESP32 #ifdef ESP32
void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
#elif defined(ESP8266) #elif defined(ESP8266)
WiFiEventHandler _onStationModeGotIPHandler; WiFiEventHandler _onStationModeGotIPHandler;
void onStationModeGotIP(const WiFiEventStationModeGotIP& event); void onStationModeGotIP(const WiFiEventStationModeGotIP & event);
#endif #endif
}; };
#endif // end OTASettingsService_h #endif // end OTASettingsService_h

View File

@@ -1,13 +1,12 @@
#include <RestartService.h> #include <RestartService.h>
RestartService::RestartService(AsyncWebServer* server, SecurityManager* securityManager) { RestartService::RestartService(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(RESTART_SERVICE_PATH, server->on(RESTART_SERVICE_PATH,
HTTP_POST, HTTP_POST,
securityManager->wrapRequest(std::bind(&RestartService::restart, this, std::placeholders::_1), securityManager->wrapRequest(std::bind(&RestartService::restart, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
AuthenticationPredicates::IS_ADMIN));
} }
void RestartService::restart(AsyncWebServerRequest* request) { void RestartService::restart(AsyncWebServerRequest * request) {
request->onDisconnect(RestartService::restartNow); request->onDisconnect(RestartService::restartNow);
request->send(200); request->send(200);
} }

View File

@@ -15,17 +15,17 @@
#define RESTART_SERVICE_PATH "/rest/restart" #define RESTART_SERVICE_PATH "/rest/restart"
class RestartService { class RestartService {
public: public:
RestartService(AsyncWebServer* server, SecurityManager* securityManager); RestartService(AsyncWebServer * server, SecurityManager * securityManager);
static void restartNow() { static void restartNow() {
WiFi.disconnect(true); WiFi.disconnect(true);
delay(500); delay(500);
ESP.restart(); ESP.restart();
} }
private: private:
void restart(AsyncWebServerRequest* request); void restart(AsyncWebServerRequest * request);
}; };
#endif // end RestartService_h #endif // end RestartService_h

View File

@@ -21,82 +21,87 @@
#define MAX_JWT_SIZE 128 #define MAX_JWT_SIZE 128
class User { class User {
public: public:
String username; String username;
String password; String password;
bool admin; bool admin;
public: public:
User(String username, String password, bool admin) : username(username), password(password), admin(admin) { User(String username, String password, bool admin)
} : username(username)
, password(password)
, admin(admin) {
}
}; };
class Authentication { class Authentication {
public: public:
User* user; User * user;
boolean authenticated; boolean authenticated;
public: public:
Authentication(User& user) : user(new User(user)), authenticated(true) { Authentication(User & user)
} : user(new User(user))
Authentication() : user(nullptr), authenticated(false) { , authenticated(true) {
} }
~Authentication() { Authentication()
delete (user); : user(nullptr)
} , authenticated(false) {
}
~Authentication() {
delete (user);
}
}; };
typedef std::function<boolean(Authentication& authentication)> AuthenticationPredicate; typedef std::function<boolean(Authentication & authentication)> AuthenticationPredicate;
class AuthenticationPredicates { class AuthenticationPredicates {
public: public:
static bool NONE_REQUIRED(Authentication& authentication) { static bool NONE_REQUIRED(Authentication & authentication) {
return true; return true;
}; };
static bool IS_AUTHENTICATED(Authentication& authentication) { static bool IS_AUTHENTICATED(Authentication & authentication) {
return authentication.authenticated; return authentication.authenticated;
}; };
static bool IS_ADMIN(Authentication& authentication) { static bool IS_ADMIN(Authentication & authentication) {
return authentication.authenticated && authentication.user->admin; return authentication.authenticated && authentication.user->admin;
}; };
}; };
class SecurityManager { class SecurityManager {
public: public:
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
/* /*
* Authenticate, returning the user if found * Authenticate, returning the user if found
*/ */
virtual Authentication authenticate(const String& username, const String& password) = 0; virtual Authentication authenticate(const String & username, const String & password) = 0;
/* /*
* Generate a JWT for the user provided * Generate a JWT for the user provided
*/ */
virtual String generateJWT(User* user) = 0; virtual String generateJWT(User * user) = 0;
#endif #endif
/* /*
* Check the request header for the Authorization token * Check the request header for the Authorization token
*/ */
virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0; virtual Authentication authenticateRequest(AsyncWebServerRequest * request) = 0;
/** /**
* Filter a request with the provided predicate, only returning true if the predicate matches. * Filter a request with the provided predicate, only returning true if the predicate matches.
*/ */
virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0; virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0;
/** /**
* Wrap the provided request to provide validation against an AuthenticationPredicate. * Wrap the provided request to provide validation against an AuthenticationPredicate.
*/ */
virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
AuthenticationPredicate predicate) = 0;
/** /**
* Wrap the provided json request callback to provide validation against an AuthenticationPredicate. * Wrap the provided json request callback to provide validation against an AuthenticationPredicate.
*/ */
virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
AuthenticationPredicate predicate) = 0;
}; };
#endif // end SecurityManager_h #endif // end SecurityManager_h

View File

@@ -2,140 +2,137 @@
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
_httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this), : _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this)
_fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE), , _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
_jwtHandler(FACTORY_JWT_SECRET) { , _jwtHandler(FACTORY_JWT_SECRET) {
addUpdateHandler([&](const String& originId) { configureJWTHandler(); }, false); addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
} }
void SecuritySettingsService::begin() { void SecuritySettingsService::begin() {
_fsPersistence.readFromFS(); _fsPersistence.readFromFS();
configureJWTHandler(); configureJWTHandler();
} }
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) { Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest * request) {
AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER); AsyncWebHeader * authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
if (authorizationHeader) { if (authorizationHeader) {
String value = authorizationHeader->value(); String value = authorizationHeader->value();
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) { if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN); value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
return authenticateJWT(value); return authenticateJWT(value);
}
} else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) {
AsyncWebParameter * tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER);
String value = tokenParamater->value();
return authenticateJWT(value);
} }
} else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) { return Authentication();
AsyncWebParameter* tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER);
String value = tokenParamater->value();
return authenticateJWT(value);
}
return Authentication();
} }
void SecuritySettingsService::configureJWTHandler() { void SecuritySettingsService::configureJWTHandler() {
_jwtHandler.setSecret(_state.jwtSecret); _jwtHandler.setSecret(_state.jwtSecret);
} }
Authentication SecuritySettingsService::authenticateJWT(String& jwt) { Authentication SecuritySettingsService::authenticateJWT(String & jwt) {
DynamicJsonDocument payloadDocument(MAX_JWT_SIZE); DynamicJsonDocument payloadDocument(MAX_JWT_SIZE);
_jwtHandler.parseJWT(jwt, payloadDocument); _jwtHandler.parseJWT(jwt, payloadDocument);
if (payloadDocument.is<JsonObject>()) { if (payloadDocument.is<JsonObject>()) {
JsonObject parsedPayload = payloadDocument.as<JsonObject>(); JsonObject parsedPayload = payloadDocument.as<JsonObject>();
String username = parsedPayload["username"]; String username = parsedPayload["username"];
for (User _user : _state.users) {
if (_user.username == username && validatePayload(parsedPayload, &_user)) {
return Authentication(_user);
}
}
}
return Authentication();
}
Authentication SecuritySettingsService::authenticate(const String & username, const String & password) {
for (User _user : _state.users) { for (User _user : _state.users) {
if (_user.username == username && validatePayload(parsedPayload, &_user)) { if (_user.username == username && _user.password == password) {
return Authentication(_user); return Authentication(_user);
} }
} }
} return Authentication();
return Authentication();
} }
Authentication SecuritySettingsService::authenticate(const String& username, const String& password) { inline void populateJWTPayload(JsonObject & payload, User * user) {
for (User _user : _state.users) { payload["username"] = user->username;
if (_user.username == username && _user.password == password) { payload["admin"] = user->admin;
return Authentication(_user); payload["version"] = EMSESP_APP_VERSION; // proddy added
}
}
return Authentication();
} }
inline void populateJWTPayload(JsonObject& payload, User* user) { boolean SecuritySettingsService::validatePayload(JsonObject & parsedPayload, User * user) {
payload["username"] = user->username; DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
payload["admin"] = user->admin; JsonObject payload = jsonDocument.to<JsonObject>();
payload["version"] = EMSESP_APP_VERSION; // proddy added populateJWTPayload(payload, user);
return payload == parsedPayload;
} }
boolean SecuritySettingsService::validatePayload(JsonObject& parsedPayload, User* user) { String SecuritySettingsService::generateJWT(User * user) {
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
JsonObject payload = jsonDocument.to<JsonObject>(); JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user); populateJWTPayload(payload, user);
return payload == parsedPayload; return _jwtHandler.buildJWT(payload);
}
String SecuritySettingsService::generateJWT(User* user) {
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user);
return _jwtHandler.buildJWT(payload);
} }
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) { ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
return [this, predicate](AsyncWebServerRequest* request) { return [this, predicate](AsyncWebServerRequest * request) {
Authentication authentication = authenticateRequest(request); Authentication authentication = authenticateRequest(request);
return predicate(authentication); return predicate(authentication);
}; };
} }
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
AuthenticationPredicate predicate) { return [this, onRequest, predicate](AsyncWebServerRequest * request) {
return [this, onRequest, predicate](AsyncWebServerRequest* request) { Authentication authentication = authenticateRequest(request);
Authentication authentication = authenticateRequest(request); if (!predicate(authentication)) {
if (!predicate(authentication)) { request->send(401);
request->send(401); return;
return; }
} onRequest(request);
onRequest(request); };
};
} }
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
AuthenticationPredicate predicate) { return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant & json) {
return [this, onRequest, predicate](AsyncWebServerRequest* request, JsonVariant& json) { Authentication authentication = authenticateRequest(request);
Authentication authentication = authenticateRequest(request); if (!predicate(authentication)) {
if (!predicate(authentication)) { request->send(401);
request->send(401); return;
return; }
} onRequest(request, json);
onRequest(request, json); };
};
} }
#else #else
User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true); User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : SecurityManager() { SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
: SecurityManager() {
} }
SecuritySettingsService::~SecuritySettingsService() { SecuritySettingsService::~SecuritySettingsService() {
} }
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) { ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
return [this, predicate](AsyncWebServerRequest* request) { return true; }; return [this, predicate](AsyncWebServerRequest * request) { return true; };
} }
// Return the admin user on all request - disabling security features // Return the admin user on all request - disabling security features
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) { Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest * request) {
return Authentication(ADMIN_USER); return Authentication(ADMIN_USER);
} }
// Return the function unwrapped // Return the function unwrapped
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
AuthenticationPredicate predicate) { return onRequest;
return onRequest;
} }
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
AuthenticationPredicate predicate) { return onRequest;
return onRequest;
} }
#endif #endif

View File

@@ -30,87 +30,87 @@
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
class SecuritySettings { class SecuritySettings {
public: public:
String jwtSecret; String jwtSecret;
std::list<User> users; std::list<User> users;
static void read(SecuritySettings& settings, JsonObject& root) { static void read(SecuritySettings & settings, JsonObject & root) {
// secret // secret
root["jwt_secret"] = settings.jwtSecret; root["jwt_secret"] = settings.jwtSecret;
// users // users
JsonArray users = root.createNestedArray("users"); JsonArray users = root.createNestedArray("users");
for (User user : settings.users) { for (User user : settings.users) {
JsonObject userRoot = users.createNestedObject(); JsonObject userRoot = users.createNestedObject();
userRoot["username"] = user.username; userRoot["username"] = user.username;
userRoot["password"] = user.password; userRoot["password"] = user.password;
userRoot["admin"] = user.admin; userRoot["admin"] = user.admin;
}
} }
}
static StateUpdateResult update(JsonObject& root, SecuritySettings& settings) { static StateUpdateResult update(JsonObject & root, SecuritySettings & settings) {
// secret // secret
settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET; settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET;
// users // users
settings.users.clear(); settings.users.clear();
if (root["users"].is<JsonArray>()) { if (root["users"].is<JsonArray>()) {
for (JsonVariant user : root["users"].as<JsonArray>()) { for (JsonVariant user : root["users"].as<JsonArray>()) {
settings.users.push_back(User(user["username"], user["password"], user["admin"])); settings.users.push_back(User(user["username"], user["password"], user["admin"]));
} }
} else { } else {
settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true)); settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true));
settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false)); settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false));
}
return StateUpdateResult::CHANGED;
} }
return StateUpdateResult::CHANGED;
}
}; };
class SecuritySettingsService : public StatefulService<SecuritySettings>, public SecurityManager { class SecuritySettingsService : public StatefulService<SecuritySettings>, public SecurityManager {
public: public:
SecuritySettingsService(AsyncWebServer* server, FS* fs); SecuritySettingsService(AsyncWebServer * server, FS * fs);
void begin(); void begin();
// Functions to implement SecurityManager // Functions to implement SecurityManager
Authentication authenticate(const String& username, const String& password); Authentication authenticate(const String & username, const String & password);
Authentication authenticateRequest(AsyncWebServerRequest* request); Authentication authenticateRequest(AsyncWebServerRequest * request);
String generateJWT(User* user); String generateJWT(User * user);
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate); ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate);
private: private:
HttpEndpoint<SecuritySettings> _httpEndpoint; HttpEndpoint<SecuritySettings> _httpEndpoint;
FSPersistence<SecuritySettings> _fsPersistence; FSPersistence<SecuritySettings> _fsPersistence;
ArduinoJsonJWT _jwtHandler; ArduinoJsonJWT _jwtHandler;
void configureJWTHandler(); void configureJWTHandler();
/* /*
* Lookup the user by JWT * Lookup the user by JWT
*/ */
Authentication authenticateJWT(String& jwt); Authentication authenticateJWT(String & jwt);
/* /*
* Verify the payload is correct * Verify the payload is correct
*/ */
boolean validatePayload(JsonObject& parsedPayload, User* user); boolean validatePayload(JsonObject & parsedPayload, User * user);
}; };
#else #else
class SecuritySettingsService : public SecurityManager { class SecuritySettingsService : public SecurityManager {
public: public:
SecuritySettingsService(AsyncWebServer* server, FS* fs); SecuritySettingsService(AsyncWebServer * server, FS * fs);
~SecuritySettingsService(); ~SecuritySettingsService();
// minimal set of functions to support framework with security settings disabled // minimal set of functions to support framework with security settings disabled
Authentication authenticateRequest(AsyncWebServerRequest* request); Authentication authenticateRequest(AsyncWebServerRequest * request);
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate); ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
}; };
#endif // end FT_ENABLED(FT_SECURITY) #endif // end FT_ENABLED(FT_SECURITY)
#endif // end SecuritySettingsService_h #endif // end SecuritySettingsService_h

View File

@@ -16,133 +16,137 @@
#endif #endif
enum class StateUpdateResult { enum class StateUpdateResult {
CHANGED = 0, // The update changed the state and propagation should take place if required CHANGED = 0, // The update changed the state and propagation should take place if required
UNCHANGED, // The state was unchanged, propagation should not take place UNCHANGED, // The state was unchanged, propagation should not take place
ERROR // There was a problem updating the state, propagation should not take place ERROR // There was a problem updating the state, propagation should not take place
}; };
template <typename T> template <typename T>
using JsonStateUpdater = std::function<StateUpdateResult(JsonObject& root, T& settings)>; using JsonStateUpdater = std::function<StateUpdateResult(JsonObject & root, T & settings)>;
template <typename T> template <typename T>
using JsonStateReader = std::function<void(T& settings, JsonObject& root)>; using JsonStateReader = std::function<void(T & settings, JsonObject & root)>;
typedef size_t update_handler_id_t; typedef size_t update_handler_id_t;
typedef std::function<void(const String& originId)> StateUpdateCallback; typedef std::function<void(const String & originId)> StateUpdateCallback;
typedef struct StateUpdateHandlerInfo { typedef struct StateUpdateHandlerInfo {
static update_handler_id_t currentUpdatedHandlerId; static update_handler_id_t currentUpdatedHandlerId;
update_handler_id_t _id; update_handler_id_t _id;
StateUpdateCallback _cb; StateUpdateCallback _cb;
bool _allowRemove; bool _allowRemove;
StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove)
_id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){}; : _id(++currentUpdatedHandlerId)
, _cb(cb)
, _allowRemove(allowRemove){};
} StateUpdateHandlerInfo_t; } StateUpdateHandlerInfo_t;
template <class T> template <class T>
class StatefulService { class StatefulService {
public: public:
template <typename... Args> template <typename... Args>
#ifdef ESP32 #ifdef ESP32
StatefulService(Args&&... args) : StatefulService(Args &&... args)
_state(std::forward<Args>(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) { : _state(std::forward<Args>(args)...)
} , _accessMutex(xSemaphoreCreateRecursiveMutex()) {
}
#else #else
StatefulService(Args&&... args) : _state(std::forward<Args>(args)...) { StatefulService(Args &&... args)
} : _state(std::forward<Args>(args)...) {
}
#endif #endif
update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) { update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) {
if (!cb) { if (!cb) {
return 0; return 0;
}
StateUpdateHandlerInfo_t updateHandler(cb, allowRemove);
_updateHandlers.push_back(updateHandler);
return updateHandler._id;
} }
StateUpdateHandlerInfo_t updateHandler(cb, allowRemove);
_updateHandlers.push_back(updateHandler);
return updateHandler._id;
}
void removeUpdateHandler(update_handler_id_t id) { void removeUpdateHandler(update_handler_id_t id) {
for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) { for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) {
if ((*i)._allowRemove && (*i)._id == id) { if ((*i)._allowRemove && (*i)._id == id) {
i = _updateHandlers.erase(i); i = _updateHandlers.erase(i);
} else { } else {
++i; ++i;
} }
}
} }
}
StateUpdateResult update(std::function<StateUpdateResult(T&)> stateUpdater, const String& originId) { StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater, const String & originId) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(_state); StateUpdateResult result = stateUpdater(_state);
endTransaction(); endTransaction();
if (result == StateUpdateResult::CHANGED) { if (result == StateUpdateResult::CHANGED) {
callUpdateHandlers(originId); callUpdateHandlers(originId);
}
return result;
} }
return result;
}
StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T&)> stateUpdater) { StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T &)> stateUpdater) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(_state); StateUpdateResult result = stateUpdater(_state);
endTransaction(); endTransaction();
return result; return result;
}
StateUpdateResult update(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater, const String& originId) {
beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction();
if (result == StateUpdateResult::CHANGED) {
callUpdateHandlers(originId);
} }
return result;
}
StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater) { StateUpdateResult update(JsonObject & jsonObject, JsonStateUpdater<T> stateUpdater, const String & originId) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state); StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction(); endTransaction();
return result; if (result == StateUpdateResult::CHANGED) {
} callUpdateHandlers(originId);
}
void read(std::function<void(T&)> stateReader) { return result;
beginTransaction();
stateReader(_state);
endTransaction();
}
void read(JsonObject& jsonObject, JsonStateReader<T> stateReader) {
beginTransaction();
stateReader(_state, jsonObject);
endTransaction();
}
void callUpdateHandlers(const String& originId) {
for (const StateUpdateHandlerInfo_t& updateHandler : _updateHandlers) {
updateHandler._cb(originId);
} }
}
protected: StateUpdateResult updateWithoutPropagation(JsonObject & jsonObject, JsonStateUpdater<T> stateUpdater) {
T _state; beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction();
return result;
}
inline void beginTransaction() { void read(std::function<void(T &)> stateReader) {
beginTransaction();
stateReader(_state);
endTransaction();
}
void read(JsonObject & jsonObject, JsonStateReader<T> stateReader) {
beginTransaction();
stateReader(_state, jsonObject);
endTransaction();
}
void callUpdateHandlers(const String & originId) {
for (const StateUpdateHandlerInfo_t & updateHandler : _updateHandlers) {
updateHandler._cb(originId);
}
}
protected:
T _state;
inline void beginTransaction() {
#ifdef ESP32 #ifdef ESP32
xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY);
#endif #endif
} }
inline void endTransaction() { inline void endTransaction() {
#ifdef ESP32 #ifdef ESP32
xSemaphoreGiveRecursive(_accessMutex); xSemaphoreGiveRecursive(_accessMutex);
#endif #endif
} }
private: private:
#ifdef ESP32 #ifdef ESP32
SemaphoreHandle_t _accessMutex; SemaphoreHandle_t _accessMutex;
#endif #endif
std::list<StateUpdateHandlerInfo_t> _updateHandlers; std::list<StateUpdateHandlerInfo_t> _updateHandlers;
}; };
#endif // end StatefulService_h #endif // end StatefulService_h

View File

@@ -17,7 +17,7 @@
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h> #include <SecurityManager.h>
#include <uuid/log.h> // proddy added #include <uuid/log.h> // proddy added
#include "../../src/system.h" // proddy added #include "../../src/system.h" // proddy added
#define MAX_ESP_STATUS_SIZE 1024 #define MAX_ESP_STATUS_SIZE 1024

View File

@@ -1,85 +1,80 @@
#include <UploadFirmwareService.h> #include <UploadFirmwareService.h>
UploadFirmwareService::UploadFirmwareService(AsyncWebServer* server, SecurityManager* securityManager) : UploadFirmwareService::UploadFirmwareService(AsyncWebServer * server, SecurityManager * securityManager)
_securityManager(securityManager) { : _securityManager(securityManager) {
server->on(UPLOAD_FIRMWARE_PATH, server->on(UPLOAD_FIRMWARE_PATH,
HTTP_POST, HTTP_POST,
std::bind(&UploadFirmwareService::uploadComplete, this, std::placeholders::_1), std::bind(&UploadFirmwareService::uploadComplete, this, std::placeholders::_1),
std::bind(&UploadFirmwareService::handleUpload, std::bind(&UploadFirmwareService::handleUpload,
this, this,
std::placeholders::_1, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_4,
std::placeholders::_5, std::placeholders::_5,
std::placeholders::_6)); std::placeholders::_6));
#ifdef ESP8266 #ifdef ESP8266
Update.runAsync(true); Update.runAsync(true);
#endif #endif
} }
void UploadFirmwareService::handleUpload(AsyncWebServerRequest* request, void UploadFirmwareService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
const String& filename, if (!index) {
size_t index, Authentication authentication = _securityManager->authenticateRequest(request);
uint8_t* data, if (AuthenticationPredicates::IS_ADMIN(authentication)) {
size_t len, if (Update.begin(request->contentLength())) {
bool final) { // success, let's make sure we end the update if the client hangs up
if (!index) { request->onDisconnect(UploadFirmwareService::handleEarlyDisconnect);
Authentication authentication = _securityManager->authenticateRequest(request); } else {
if (AuthenticationPredicates::IS_ADMIN(authentication)) { // failed to begin, send an error response
if (Update.begin(request->contentLength())) { Update.printError(Serial);
// success, let's make sure we end the update if the client hangs up handleError(request, 500);
request->onDisconnect(UploadFirmwareService::handleEarlyDisconnect); }
} else { } else {
// failed to begin, send an error response // send the forbidden response
Update.printError(Serial); handleError(request, 403);
handleError(request, 500); }
}
} else {
// send the forbidden response
handleError(request, 403);
} }
}
// if we haven't delt with an error, continue with the update // if we haven't delt with an error, continue with the update
if (!request->_tempObject) { if (!request->_tempObject) {
if (Update.write(data, len) != len) { if (Update.write(data, len) != len) {
Update.printError(Serial); Update.printError(Serial);
handleError(request, 500); handleError(request, 500);
}
if (final) {
if (!Update.end(true)) {
Update.printError(Serial);
handleError(request, 500);
}
}
} }
if (final) {
if (!Update.end(true)) {
Update.printError(Serial);
handleError(request, 500);
}
}
}
} }
void UploadFirmwareService::uploadComplete(AsyncWebServerRequest* request) { void UploadFirmwareService::uploadComplete(AsyncWebServerRequest * request) {
// if no error, send the success response // if no error, send the success response
if (!request->_tempObject) { if (!request->_tempObject) {
request->onDisconnect(RestartService::restartNow); request->onDisconnect(RestartService::restartNow);
AsyncWebServerResponse* response = request->beginResponse(200); AsyncWebServerResponse * response = request->beginResponse(200);
request->send(response);
}
}
void UploadFirmwareService::handleError(AsyncWebServerRequest * request, int code) {
// if we have had an error already, do nothing
if (request->_tempObject) {
return;
}
// send the error code to the client and record the error code in the temp object
request->_tempObject = new int(code);
AsyncWebServerResponse * response = request->beginResponse(code);
request->send(response); request->send(response);
}
}
void UploadFirmwareService::handleError(AsyncWebServerRequest* request, int code) {
// if we have had an error already, do nothing
if (request->_tempObject) {
return;
}
// send the error code to the client and record the error code in the temp object
request->_tempObject = new int(code);
AsyncWebServerResponse* response = request->beginResponse(code);
request->send(response);
} }
void UploadFirmwareService::handleEarlyDisconnect() { void UploadFirmwareService::handleEarlyDisconnect() {
#ifdef ESP32 #ifdef ESP32
Update.abort(); Update.abort();
#elif defined(ESP8266) #elif defined(ESP8266)
Update.end(); Update.end();
#endif #endif
} }

View File

@@ -19,20 +19,15 @@
#define UPLOAD_FIRMWARE_PATH "/rest/uploadFirmware" #define UPLOAD_FIRMWARE_PATH "/rest/uploadFirmware"
class UploadFirmwareService { class UploadFirmwareService {
public: public:
UploadFirmwareService(AsyncWebServer* server, SecurityManager* securityManager); UploadFirmwareService(AsyncWebServer * server, SecurityManager * securityManager);
private: private:
SecurityManager* _securityManager; SecurityManager * _securityManager;
void handleUpload(AsyncWebServerRequest* request, void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
const String& filename, void uploadComplete(AsyncWebServerRequest * request);
size_t index, void handleError(AsyncWebServerRequest * request, int code);
uint8_t* data, static void handleEarlyDisconnect();
size_t len,
bool final);
void uploadComplete(AsyncWebServerRequest* request);
void handleError(AsyncWebServerRequest* request, int code);
static void handleEarlyDisconnect();
}; };
#endif // end UploadFirmwareService_h #endif // end UploadFirmwareService_h

View File

@@ -12,262 +12,217 @@
template <class T> template <class T>
class WebSocketConnector { class WebSocketConnector {
protected: protected:
StatefulService<T>* _statefulService; StatefulService<T> * _statefulService;
AsyncWebServer* _server; AsyncWebServer * _server;
AsyncWebSocket _webSocket; AsyncWebSocket _webSocket;
size_t _bufferSize; size_t _bufferSize;
WebSocketConnector(StatefulService<T>* statefulService, WebSocketConnector(StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
char const* webSocketPath, const char * webSocketPath,
SecurityManager* securityManager, SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate, AuthenticationPredicate authenticationPredicate,
size_t bufferSize) : size_t bufferSize)
_statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) { : _statefulService(statefulService)
_webSocket.setFilter(securityManager->filterRequest(authenticationPredicate)); , _server(server)
_webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, , _webSocket(webSocketPath)
this, , _bufferSize(bufferSize) {
std::placeholders::_1, _webSocket.setFilter(securityManager->filterRequest(authenticationPredicate));
std::placeholders::_2, _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent,
std::placeholders::_3, this,
std::placeholders::_4, std::placeholders::_1,
std::placeholders::_5, std::placeholders::_2,
std::placeholders::_6)); std::placeholders::_3,
_server->addHandler(&_webSocket); std::placeholders::_4,
_server->on(webSocketPath, HTTP_GET, std::bind(&WebSocketConnector::forbidden, this, std::placeholders::_1)); std::placeholders::_5,
} std::placeholders::_6));
_server->addHandler(&_webSocket);
_server->on(webSocketPath, HTTP_GET, std::bind(&WebSocketConnector::forbidden, this, std::placeholders::_1));
}
WebSocketConnector(StatefulService<T>* statefulService, WebSocketConnector(StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize)
AsyncWebServer* server, : _statefulService(statefulService)
char const* webSocketPath, , _server(server)
size_t bufferSize) : , _webSocket(webSocketPath)
_statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) { , _bufferSize(bufferSize) {
_webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent,
this, this,
std::placeholders::_1, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_4,
std::placeholders::_5, std::placeholders::_5,
std::placeholders::_6)); std::placeholders::_6));
_server->addHandler(&_webSocket); _server->addHandler(&_webSocket);
} }
virtual void onWSEvent(AsyncWebSocket* server, virtual void onWSEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len) = 0;
AsyncWebSocketClient* client,
AwsEventType type,
void* arg,
uint8_t* data,
size_t len) = 0;
String clientId(AsyncWebSocketClient* client) { String clientId(AsyncWebSocketClient * client) {
return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->id()); return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->id());
} }
private: private:
void forbidden(AsyncWebServerRequest* request) { void forbidden(AsyncWebServerRequest * request) {
request->send(403); request->send(403);
} }
}; };
template <class T> template <class T>
class WebSocketTx : virtual public WebSocketConnector<T> { class WebSocketTx : virtual public WebSocketConnector<T> {
public: public:
WebSocketTx(JsonStateReader<T> stateReader, WebSocketTx(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
char const* webSocketPath, const char * webSocketPath,
SecurityManager* securityManager, SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
WebSocketConnector<T>(statefulService, : WebSocketConnector<T>(statefulService, server, webSocketPath, securityManager, authenticationPredicate, bufferSize)
server, , _stateReader(stateReader) {
webSocketPath, WebSocketConnector<T>::_statefulService->addUpdateHandler([&](const String & originId) { transmitData(nullptr, originId); }, false);
securityManager,
authenticationPredicate,
bufferSize),
_stateReader(stateReader) {
WebSocketConnector<T>::_statefulService->addUpdateHandler(
[&](const String& originId) { transmitData(nullptr, originId); }, false);
}
WebSocketTx(JsonStateReader<T> stateReader,
StatefulService<T>* statefulService,
AsyncWebServer* server,
char const* webSocketPath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize), _stateReader(stateReader) {
WebSocketConnector<T>::_statefulService->addUpdateHandler(
[&](const String& originId) { transmitData(nullptr, originId); }, false);
}
protected:
virtual void onWSEvent(AsyncWebSocket* server,
AsyncWebSocketClient* client,
AwsEventType type,
void* arg,
uint8_t* data,
size_t len) {
if (type == WS_EVT_CONNECT) {
// when a client connects, we transmit it's id and the current payload
transmitId(client);
transmitData(client, WEB_SOCKET_ORIGIN);
} }
}
private: WebSocketTx(JsonStateReader<T> stateReader,
JsonStateReader<T> _stateReader; StatefulService<T> * statefulService,
AsyncWebServer * server,
void transmitId(AsyncWebSocketClient* client) { const char * webSocketPath,
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WEB_SOCKET_CLIENT_ID_MSG_SIZE); size_t bufferSize = DEFAULT_BUFFER_SIZE)
JsonObject root = jsonDocument.to<JsonObject>(); : WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
root["type"] = "id"; , _stateReader(stateReader) {
root["id"] = WebSocketConnector<T>::clientId(client); WebSocketConnector<T>::_statefulService->addUpdateHandler([&](const String & originId) { transmitData(nullptr, originId); }, false);
size_t len = measureJson(jsonDocument);
AsyncWebSocketMessageBuffer* buffer = WebSocketConnector<T>::_webSocket.makeBuffer(len);
if (buffer) {
serializeJson(jsonDocument, (char*)buffer->get(), len + 1);
client->text(buffer);
} }
}
/** protected:
virtual void onWSEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len) {
if (type == WS_EVT_CONNECT) {
// when a client connects, we transmit it's id and the current payload
transmitId(client);
transmitData(client, WEB_SOCKET_ORIGIN);
}
}
private:
JsonStateReader<T> _stateReader;
void transmitId(AsyncWebSocketClient * client) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WEB_SOCKET_CLIENT_ID_MSG_SIZE);
JsonObject root = jsonDocument.to<JsonObject>();
root["type"] = "id";
root["id"] = WebSocketConnector<T>::clientId(client);
size_t len = measureJson(jsonDocument);
AsyncWebSocketMessageBuffer * buffer = WebSocketConnector<T>::_webSocket.makeBuffer(len);
if (buffer) {
serializeJson(jsonDocument, (char *)buffer->get(), len + 1);
client->text(buffer);
}
}
/**
* Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if * Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if
* specified. * specified.
* *
* Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach * Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach
* simplifies the client and the server implementation but may not be sufficent for all use-cases. * simplifies the client and the server implementation but may not be sufficent for all use-cases.
*/ */
void transmitData(AsyncWebSocketClient* client, const String& originId) { void transmitData(AsyncWebSocketClient * client, const String & originId) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector<T>::_bufferSize); DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector<T>::_bufferSize);
JsonObject root = jsonDocument.to<JsonObject>(); JsonObject root = jsonDocument.to<JsonObject>();
root["type"] = "payload"; root["type"] = "payload";
root["origin_id"] = originId; root["origin_id"] = originId;
JsonObject payload = root.createNestedObject("payload"); JsonObject payload = root.createNestedObject("payload");
WebSocketConnector<T>::_statefulService->read(payload, _stateReader); WebSocketConnector<T>::_statefulService->read(payload, _stateReader);
size_t len = measureJson(jsonDocument); size_t len = measureJson(jsonDocument);
AsyncWebSocketMessageBuffer* buffer = WebSocketConnector<T>::_webSocket.makeBuffer(len); AsyncWebSocketMessageBuffer * buffer = WebSocketConnector<T>::_webSocket.makeBuffer(len);
if (buffer) { if (buffer) {
serializeJson(jsonDocument, (char*)buffer->get(), len + 1); serializeJson(jsonDocument, (char *)buffer->get(), len + 1);
if (client) { if (client) {
client->text(buffer); client->text(buffer);
} else { } else {
WebSocketConnector<T>::_webSocket.textAll(buffer); WebSocketConnector<T>::_webSocket.textAll(buffer);
} }
}
} }
}
}; };
template <class T> template <class T>
class WebSocketRx : virtual public WebSocketConnector<T> { class WebSocketRx : virtual public WebSocketConnector<T> {
public: public:
WebSocketRx(JsonStateUpdater<T> stateUpdater, WebSocketRx(JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
char const* webSocketPath, const char * webSocketPath,
SecurityManager* securityManager, SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
WebSocketConnector<T>(statefulService, : WebSocketConnector<T>(statefulService, server, webSocketPath, securityManager, authenticationPredicate, bufferSize)
server, , _stateUpdater(stateUpdater) {
webSocketPath,
securityManager,
authenticationPredicate,
bufferSize),
_stateUpdater(stateUpdater) {
}
WebSocketRx(JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService,
AsyncWebServer* server,
char const* webSocketPath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize), _stateUpdater(stateUpdater) {
}
protected:
virtual void onWSEvent(AsyncWebSocket* server,
AsyncWebSocketClient* client,
AwsEventType type,
void* arg,
uint8_t* data,
size_t len) {
if (type == WS_EVT_DATA) {
AwsFrameInfo* info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len) {
if (info->opcode == WS_TEXT) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector<T>::_bufferSize);
DeserializationError error = deserializeJson(jsonDocument, (char*)data);
if (!error && jsonDocument.is<JsonObject>()) {
JsonObject jsonObject = jsonDocument.as<JsonObject>();
WebSocketConnector<T>::_statefulService->update(
jsonObject, _stateUpdater, WebSocketConnector<T>::clientId(client));
}
}
}
} }
}
private: WebSocketRx(JsonStateUpdater<T> stateUpdater,
JsonStateUpdater<T> _stateUpdater; StatefulService<T> * statefulService,
AsyncWebServer * server,
const char * webSocketPath,
size_t bufferSize = DEFAULT_BUFFER_SIZE)
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
, _stateUpdater(stateUpdater) {
}
protected:
virtual void onWSEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len) {
if (type == WS_EVT_DATA) {
AwsFrameInfo * info = (AwsFrameInfo *)arg;
if (info->final && info->index == 0 && info->len == len) {
if (info->opcode == WS_TEXT) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector<T>::_bufferSize);
DeserializationError error = deserializeJson(jsonDocument, (char *)data);
if (!error && jsonDocument.is<JsonObject>()) {
JsonObject jsonObject = jsonDocument.as<JsonObject>();
WebSocketConnector<T>::_statefulService->update(jsonObject, _stateUpdater, WebSocketConnector<T>::clientId(client));
}
}
}
}
}
private:
JsonStateUpdater<T> _stateUpdater;
}; };
template <class T> template <class T>
class WebSocketTxRx : public WebSocketTx<T>, public WebSocketRx<T> { class WebSocketTxRx : public WebSocketTx<T>, public WebSocketRx<T> {
public: public:
WebSocketTxRx(JsonStateReader<T> stateReader, WebSocketTxRx(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
char const* webSocketPath, const char * webSocketPath,
SecurityManager* securityManager, SecurityManager * securityManager,
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
WebSocketConnector<T>(statefulService, : WebSocketConnector<T>(statefulService, server, webSocketPath, securityManager, authenticationPredicate, bufferSize)
server, , WebSocketTx<T>(stateReader, statefulService, server, webSocketPath, securityManager, authenticationPredicate, bufferSize)
webSocketPath, , WebSocketRx<T>(stateUpdater, statefulService, server, webSocketPath, securityManager, authenticationPredicate, bufferSize) {
securityManager, }
authenticationPredicate,
bufferSize),
WebSocketTx<T>(stateReader,
statefulService,
server,
webSocketPath,
securityManager,
authenticationPredicate,
bufferSize),
WebSocketRx<T>(stateUpdater,
statefulService,
server,
webSocketPath,
securityManager,
authenticationPredicate,
bufferSize) {
}
WebSocketTxRx(JsonStateReader<T> stateReader, WebSocketTxRx(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T>* statefulService, StatefulService<T> * statefulService,
AsyncWebServer* server, AsyncWebServer * server,
char const* webSocketPath, const char * webSocketPath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE)
WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize), : WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
WebSocketTx<T>(stateReader, statefulService, server, webSocketPath, bufferSize), , WebSocketTx<T>(stateReader, statefulService, server, webSocketPath, bufferSize)
WebSocketRx<T>(stateUpdater, statefulService, server, webSocketPath, bufferSize) { , WebSocketRx<T>(stateUpdater, statefulService, server, webSocketPath, bufferSize) {
} }
protected: protected:
void onWSEvent(AsyncWebSocket* server, void onWSEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len) {
AsyncWebSocketClient* client, WebSocketRx<T>::onWSEvent(server, client, type, arg, data, len);
AwsEventType type, WebSocketTx<T>::onWSEvent(server, client, type, arg, data, len);
void* arg, }
uint8_t* data,
size_t len) {
WebSocketRx<T>::onWSEvent(server, client, type, arg, data, len);
WebSocketTx<T>::onWSEvent(server, client, type, arg, data, len);
}
}; };
#endif #endif

View File

@@ -1,49 +1,47 @@
#include <WiFiScanner.h> #include <WiFiScanner.h>
WiFiScanner::WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager) { WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(SCAN_NETWORKS_SERVICE_PATH, server->on(SCAN_NETWORKS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1), securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
AuthenticationPredicates::IS_ADMIN)); server->on(LIST_NETWORKS_SERVICE_PATH,
server->on(LIST_NETWORKS_SERVICE_PATH, HTTP_GET,
HTTP_GET, securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1),
AuthenticationPredicates::IS_ADMIN));
}; };
void WiFiScanner::scanNetworks(AsyncWebServerRequest* request) { void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
if (WiFi.scanComplete() != -1) { if (WiFi.scanComplete() != -1) {
WiFi.scanDelete(); WiFi.scanDelete();
WiFi.scanNetworks(true); WiFi.scanNetworks(true);
} }
request->send(202); request->send(202);
} }
void WiFiScanner::listNetworks(AsyncWebServerRequest* request) { void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
int numNetworks = WiFi.scanComplete(); int numNetworks = WiFi.scanComplete();
if (numNetworks > -1) { if (numNetworks > -1) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
JsonArray networks = root.createNestedArray("networks"); JsonArray networks = root.createNestedArray("networks");
for (int i = 0; i < numNetworks; i++) { for (int i = 0; i < numNetworks; i++) {
JsonObject network = networks.createNestedObject(); JsonObject network = networks.createNestedObject();
network["rssi"] = WiFi.RSSI(i); network["rssi"] = WiFi.RSSI(i);
network["ssid"] = WiFi.SSID(i); network["ssid"] = WiFi.SSID(i);
network["bssid"] = WiFi.BSSIDstr(i); network["bssid"] = WiFi.BSSIDstr(i);
network["channel"] = WiFi.channel(i); network["channel"] = WiFi.channel(i);
#ifdef ESP32 #ifdef ESP32
network["encryption_type"] = (uint8_t)WiFi.encryptionType(i); network["encryption_type"] = (uint8_t)WiFi.encryptionType(i);
#elif defined(ESP8266) #elif defined(ESP8266)
network["encryption_type"] = convertEncryptionType(WiFi.encryptionType(i)); network["encryption_type"] = convertEncryptionType(WiFi.encryptionType(i));
#endif #endif
}
response->setLength();
request->send(response);
} else if (numNetworks == -1) {
request->send(202);
} else {
scanNetworks(request);
} }
response->setLength();
request->send(response);
} else if (numNetworks == -1) {
request->send(202);
} else {
scanNetworks(request);
}
} }
#ifdef ESP8266 #ifdef ESP8266
@@ -53,18 +51,18 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest* request) {
* This allows us to use a single set of mappings in the UI. * This allows us to use a single set of mappings in the UI.
*/ */
uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType) { uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType) {
switch (encryptionType) { switch (encryptionType) {
case ENC_TYPE_NONE: case ENC_TYPE_NONE:
return AUTH_OPEN; return AUTH_OPEN;
case ENC_TYPE_WEP: case ENC_TYPE_WEP:
return AUTH_WEP; return AUTH_WEP;
case ENC_TYPE_TKIP: case ENC_TYPE_TKIP:
return AUTH_WPA_PSK; return AUTH_WPA_PSK;
case ENC_TYPE_CCMP: case ENC_TYPE_CCMP:
return AUTH_WPA2_PSK; return AUTH_WPA2_PSK;
case ENC_TYPE_AUTO: case ENC_TYPE_AUTO:
return AUTH_WPA_WPA2_PSK; return AUTH_WPA_WPA2_PSK;
} }
return -1; return -1;
} }
#endif #endif

View File

@@ -20,16 +20,16 @@
#define MAX_WIFI_SCANNER_SIZE 1024 #define MAX_WIFI_SCANNER_SIZE 1024
class WiFiScanner { class WiFiScanner {
public: public:
WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager); WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager);
private: private:
void scanNetworks(AsyncWebServerRequest* request); void scanNetworks(AsyncWebServerRequest * request);
void listNetworks(AsyncWebServerRequest* request); void listNetworks(AsyncWebServerRequest * request);
#ifdef ESP8266 #ifdef ESP8266
uint8_t convertEncryptionType(uint8_t encryptionType); uint8_t convertEncryptionType(uint8_t encryptionType);
#endif #endif
}; };
#endif // end WiFiScanner_h #endif // end WiFiScanner_h

View File

@@ -74,7 +74,7 @@ void WiFiSettingsService::manageSTA() {
// configure for static IP // configure for static IP
WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
} else { } else {
// configure for DHCP // configure for DHCP
#ifdef ESP32 #ifdef ESP32
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
#elif defined(ESP8266) #elif defined(ESP8266)

View File

@@ -23,89 +23,87 @@
#endif #endif
class WiFiSettings { class WiFiSettings {
public: public:
// core wifi configuration // core wifi configuration
String ssid; String ssid;
String password; String password;
String hostname; String hostname;
bool staticIPConfig; bool staticIPConfig;
// optional configuration for static IP address // optional configuration for static IP address
IPAddress localIP; IPAddress localIP;
IPAddress gatewayIP; IPAddress gatewayIP;
IPAddress subnetMask; IPAddress subnetMask;
IPAddress dnsIP1; IPAddress dnsIP1;
IPAddress dnsIP2; IPAddress dnsIP2;
static void read(WiFiSettings& settings, JsonObject& root) { static void read(WiFiSettings & settings, JsonObject & root) {
// connection settings // connection settings
root["ssid"] = settings.ssid; root["ssid"] = settings.ssid;
root["password"] = settings.password; root["password"] = settings.password;
root["hostname"] = settings.hostname; root["hostname"] = settings.hostname;
root["static_ip_config"] = settings.staticIPConfig; root["static_ip_config"] = settings.staticIPConfig;
// extended settings // extended settings
JsonUtils::writeIP(root, "local_ip", settings.localIP); JsonUtils::writeIP(root, "local_ip", settings.localIP);
JsonUtils::writeIP(root, "gateway_ip", settings.gatewayIP); JsonUtils::writeIP(root, "gateway_ip", settings.gatewayIP);
JsonUtils::writeIP(root, "subnet_mask", settings.subnetMask); JsonUtils::writeIP(root, "subnet_mask", settings.subnetMask);
JsonUtils::writeIP(root, "dns_ip_1", settings.dnsIP1); JsonUtils::writeIP(root, "dns_ip_1", settings.dnsIP1);
JsonUtils::writeIP(root, "dns_ip_2", settings.dnsIP2); JsonUtils::writeIP(root, "dns_ip_2", settings.dnsIP2);
}
static StateUpdateResult update(JsonObject& root, WiFiSettings& settings) {
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
JsonUtils::readIP(root, "local_ip", settings.localIP);
JsonUtils::readIP(root, "gateway_ip", settings.gatewayIP);
JsonUtils::readIP(root, "subnet_mask", settings.subnetMask);
JsonUtils::readIP(root, "dns_ip_1", settings.dnsIP1);
JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2);
// Swap around the dns servers if 2 is populated but 1 is not
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 static StateUpdateResult update(JsonObject & root, WiFiSettings & settings) {
// of ipAddress, gateway and subnet. This may change to static ip only settings.ssid = root["ssid"] | FACTORY_WIFI_SSID;
// as sensible defaults can be assumed for gateway and subnet settings.password = root["password"] | FACTORY_WIFI_PASSWORD;
if (settings.staticIPConfig && settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME;
(settings.localIP == INADDR_NONE || settings.gatewayIP == INADDR_NONE || settings.subnetMask == INADDR_NONE)) { settings.staticIPConfig = root["static_ip_config"] | false;
settings.staticIPConfig = false;
// extended settings
JsonUtils::readIP(root, "local_ip", settings.localIP);
JsonUtils::readIP(root, "gateway_ip", settings.gatewayIP);
JsonUtils::readIP(root, "subnet_mask", settings.subnetMask);
JsonUtils::readIP(root, "dns_ip_1", settings.dnsIP1);
JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2);
// Swap around the dns servers if 2 is populated but 1 is not
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 (settings.staticIPConfig && (settings.localIP == INADDR_NONE || settings.gatewayIP == INADDR_NONE || settings.subnetMask == INADDR_NONE)) {
settings.staticIPConfig = false;
}
return StateUpdateResult::CHANGED;
} }
return StateUpdateResult::CHANGED;
}
}; };
class WiFiSettingsService : public StatefulService<WiFiSettings> { class WiFiSettingsService : public StatefulService<WiFiSettings> {
public: public:
WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); WiFiSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void begin(); void begin();
void loop(); void loop();
private: private:
HttpEndpoint<WiFiSettings> _httpEndpoint; HttpEndpoint<WiFiSettings> _httpEndpoint;
FSPersistence<WiFiSettings> _fsPersistence; FSPersistence<WiFiSettings> _fsPersistence;
unsigned long _lastConnectionAttempt; unsigned long _lastConnectionAttempt;
#ifdef ESP32 #ifdef ESP32
bool _stopping; bool _stopping;
void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
void onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info);
#elif defined(ESP8266) #elif defined(ESP8266)
WiFiEventHandler _onStationModeDisconnectedHandler; WiFiEventHandler _onStationModeDisconnectedHandler;
void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); void onStationModeDisconnected(const WiFiEventStationModeDisconnected & event);
#endif #endif
void reconfigureWiFiConnection(); void reconfigureWiFiConnection();
void manageSTA(); void manageSTA();
}; };
#endif // end WiFiSettingsService_h #endif // end WiFiSettingsService_h

View File

@@ -1,73 +1,72 @@
#include <WiFiStatus.h> #include <WiFiStatus.h>
WiFiStatus::WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager) { WiFiStatus::WiFiStatus(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(WIFI_STATUS_SERVICE_PATH, server->on(WIFI_STATUS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WiFiStatus::wifiStatus, this, std::placeholders::_1), securityManager->wrapRequest(std::bind(&WiFiStatus::wifiStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED));
AuthenticationPredicates::IS_AUTHENTICATED));
#ifdef ESP32 #ifdef ESP32
WiFi.onEvent(onStationModeConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED); WiFi.onEvent(onStationModeConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
#elif defined(ESP8266) #elif defined(ESP8266)
_onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected); _onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected);
_onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected); _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected);
_onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP); _onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP);
#endif #endif
} }
#ifdef ESP32 #ifdef ESP32
void WiFiStatus::onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info) { void WiFiStatus::onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
// Serial.println(F("WiFi Connected.")); // Serial.println(F("WiFi Connected."));
} }
void WiFiStatus::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { void WiFiStatus::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
// Serial.print(F("WiFi Disconnected. Reason code=")); // Serial.print(F("WiFi Disconnected. Reason code="));
// Serial.println(info.disconnected.reason); // Serial.println(info.disconnected.reason);
} }
void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
// Serial.printf_P(PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), WiFi.localIP().toString().c_str(), WiFi.getHostname()); // Serial.printf_P(PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), WiFi.localIP().toString().c_str(), WiFi.getHostname());
} }
#elif defined(ESP8266) #elif defined(ESP8266)
void WiFiStatus::onStationModeConnected(const WiFiEventStationModeConnected& event) { void WiFiStatus::onStationModeConnected(const WiFiEventStationModeConnected & event) {
// Serial.print(F("WiFi Connected. SSID=")); // Serial.print(F("WiFi Connected. SSID="));
// Serial.println(event.ssid); // Serial.println(event.ssid);
} }
void WiFiStatus::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { void WiFiStatus::onStationModeDisconnected(const WiFiEventStationModeDisconnected & event) {
// Serial.print(F("WiFi Disconnected. Reason code=")); // Serial.print(F("WiFi Disconnected. Reason code="));
// Serial.println(event.reason); // Serial.println(event.reason);
} }
void WiFiStatus::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { void WiFiStatus::onStationModeGotIP(const WiFiEventStationModeGotIP & event) {
// Serial.printf_P(PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), event.ip.toString().c_str(), WiFi.hostname().c_str()); // Serial.printf_P(PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), event.ip.toString().c_str(), WiFi.hostname().c_str());
} }
#endif #endif
void WiFiStatus::wifiStatus(AsyncWebServerRequest* request) { void WiFiStatus::wifiStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_STATUS_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_WIFI_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
wl_status_t status = WiFi.status(); wl_status_t status = WiFi.status();
root["status"] = (uint8_t)status; root["status"] = (uint8_t)status;
if (status == WL_CONNECTED) { if (status == WL_CONNECTED) {
root["local_ip"] = WiFi.localIP().toString(); root["local_ip"] = WiFi.localIP().toString();
root["mac_address"] = WiFi.macAddress(); root["mac_address"] = WiFi.macAddress();
root["rssi"] = WiFi.RSSI(); root["rssi"] = WiFi.RSSI();
root["ssid"] = WiFi.SSID(); root["ssid"] = WiFi.SSID();
root["bssid"] = WiFi.BSSIDstr(); root["bssid"] = WiFi.BSSIDstr();
root["channel"] = WiFi.channel(); root["channel"] = WiFi.channel();
root["subnet_mask"] = WiFi.subnetMask().toString(); root["subnet_mask"] = WiFi.subnetMask().toString();
root["gateway_ip"] = WiFi.gatewayIP().toString(); root["gateway_ip"] = WiFi.gatewayIP().toString();
IPAddress dnsIP1 = WiFi.dnsIP(0); IPAddress dnsIP1 = WiFi.dnsIP(0);
IPAddress dnsIP2 = WiFi.dnsIP(1); IPAddress dnsIP2 = WiFi.dnsIP(1);
if (dnsIP1 != INADDR_NONE) { if (dnsIP1 != INADDR_NONE) {
root["dns_ip_1"] = dnsIP1.toString(); root["dns_ip_1"] = dnsIP1.toString();
}
if (dnsIP2 != INADDR_NONE) {
root["dns_ip_2"] = dnsIP2.toString();
}
} }
if (dnsIP2 != INADDR_NONE) { response->setLength();
root["dns_ip_2"] = dnsIP2.toString(); request->send(response);
}
}
response->setLength();
request->send(response);
} }

View File

@@ -19,27 +19,27 @@
#define WIFI_STATUS_SERVICE_PATH "/rest/wifiStatus" #define WIFI_STATUS_SERVICE_PATH "/rest/wifiStatus"
class WiFiStatus { class WiFiStatus {
public: public:
WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager); WiFiStatus(AsyncWebServer * server, SecurityManager * securityManager);
private: private:
#ifdef ESP32 #ifdef ESP32
// static functions for logging WiFi events to the UART // static functions for logging WiFi events to the UART
static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info); static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info);
static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
#elif defined(ESP8266) #elif defined(ESP8266)
// handler refrences for logging important WiFi events over serial // handler refrences for logging important WiFi events over serial
WiFiEventHandler _onStationModeConnectedHandler; WiFiEventHandler _onStationModeConnectedHandler;
WiFiEventHandler _onStationModeDisconnectedHandler; WiFiEventHandler _onStationModeDisconnectedHandler;
WiFiEventHandler _onStationModeGotIPHandler; WiFiEventHandler _onStationModeGotIPHandler;
// static functions for logging WiFi events to the UART // static functions for logging WiFi events to the UART
static void onStationModeConnected(const WiFiEventStationModeConnected& event); static void onStationModeConnected(const WiFiEventStationModeConnected & event);
static void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); static void onStationModeDisconnected(const WiFiEventStationModeDisconnected & event);
static void onStationModeGotIP(const WiFiEventStationModeGotIP& event); static void onStationModeGotIP(const WiFiEventStationModeGotIP & event);
#endif #endif
void wifiStatus(AsyncWebServerRequest* request); void wifiStatus(AsyncWebServerRequest * request);
}; };
#endif // end WiFiStatus_h #endif // end WiFiStatus_h

View File

@@ -11,7 +11,7 @@ class FSPersistence {
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
StatefulService<T> * statefulService, StatefulService<T> * statefulService,
FS * fs, FS * fs,
char const * filePath, const char * filePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) size_t bufferSize = DEFAULT_BUFFER_SIZE)
: _stateReader(stateReader) : _stateReader(stateReader)
, _stateUpdater(stateUpdater) , _stateUpdater(stateUpdater)
@@ -52,7 +52,7 @@ class FSPersistence {
JsonStateUpdater<T> _stateUpdater; JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService; StatefulService<T> * _statefulService;
FS * _fs; FS * _fs;
char const * _filePath; const char * _filePath;
size_t _bufferSize; size_t _bufferSize;
update_handler_id_t _updateHandlerId; update_handler_id_t _updateHandlerId;