mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
auto formatting
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user