refactoring

This commit is contained in:
Proddy
2024-02-12 14:21:30 +01:00
parent ed5f0bc6d5
commit 80a3007f8b
56 changed files with 555 additions and 516 deletions

View File

@@ -1,4 +1,4 @@
#include <APSettingsService.h> #include "APSettingsService.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
@@ -9,8 +9,8 @@ APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityM
, _lastManaged(0) , _lastManaged(0)
, _reconfigureAp(false) , _reconfigureAp(false)
, _connected(0) { , _connected(0) {
addUpdateHandler([&](const String & originId) { reconfigureAP(); }, false); addUpdateHandler([this] { reconfigureAP(); }, false);
WiFi.onEvent(std::bind(&APSettingsService::WiFiEvent, this, _1)); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
} }
void APSettingsService::begin() { void APSettingsService::begin() {
@@ -53,7 +53,7 @@ void APSettingsService::reconfigureAP() {
void APSettingsService::loop() { void APSettingsService::loop() {
unsigned long currentMillis = uuid::get_uptime(); unsigned long currentMillis = uuid::get_uptime();
unsigned long manageElapsed = (uint32_t)(currentMillis - _lastManaged); unsigned long manageElapsed = static_cast<uint32_t>(currentMillis - _lastManaged);
if (manageElapsed >= MANAGE_NETWORK_DELAY) { if (manageElapsed >= MANAGE_NETWORK_DELAY) {
_lastManaged = currentMillis; _lastManaged = currentMillis;
manageAP(); manageAP();
@@ -76,7 +76,7 @@ void APSettingsService::manageAP() {
void APSettingsService::startAP() { void APSettingsService::startAP() {
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask); WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_AP, WIFI_BW_HT20); esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients); WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients);
#if CONFIG_IDF_TARGET_ESP32C3 #if CONFIG_IDF_TARGET_ESP32C3
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
@@ -108,14 +108,16 @@ void APSettingsService::handleDNS() {
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;
} }
void APSettings::read(APSettings & settings, JsonObject root) { void APSettings::read(const APSettings & settings, JsonObject root) {
root["provision_mode"] = settings.provisionMode; root["provision_mode"] = settings.provisionMode;
root["ssid"] = settings.ssid; root["ssid"] = settings.ssid;
root["password"] = settings.password; root["password"] = settings.password;
@@ -129,7 +131,8 @@ void APSettings::read(APSettings & settings, JsonObject root) {
StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) { StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
APSettings newSettings = {}; APSettings newSettings = {};
newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE; newSettings.provisionMode = static_cast<uint8_t>(root["provision_mode"] | FACTORY_AP_PROVISION_MODE);
switch (settings.provisionMode) { switch (settings.provisionMode) {
case AP_MODE_ALWAYS: case AP_MODE_ALWAYS:
case AP_MODE_DISCONNECTED: case AP_MODE_DISCONNECTED:
@@ -138,11 +141,12 @@ StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
default: default:
newSettings.provisionMode = AP_MODE_ALWAYS; newSettings.provisionMode = AP_MODE_ALWAYS;
} }
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID; newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
newSettings.password = root["password"] | FACTORY_AP_PASSWORD; newSettings.password = root["password"] | FACTORY_AP_PASSWORD;
newSettings.channel = root["channel"] | FACTORY_AP_CHANNEL; newSettings.channel = static_cast<uint8_t>(root["channel"] | FACTORY_AP_CHANNEL);
newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN; newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN;
newSettings.maxClients = root["max_clients"] | FACTORY_AP_MAX_CLIENTS; newSettings.maxClients = static_cast<uint8_t>(root["max_clients"] | FACTORY_AP_MAX_CLIENTS);
JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP); 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, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP);
@@ -151,6 +155,7 @@ StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
if (newSettings == settings) { if (newSettings == settings) {
return StateUpdateResult::UNCHANGED; return StateUpdateResult::UNCHANGED;
} }
settings = newSettings; settings = newSettings;
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
} }

View File

@@ -1,9 +1,9 @@
#ifndef APSettingsConfig_h #ifndef APSettingsConfig_h
#define APSettingsConfig_h #define APSettingsConfig_h
#include <HttpEndpoint.h> #include "HttpEndpoint.h"
#include <FSPersistence.h> #include "FSPersistence.h"
#include <JsonUtils.h> #include "JsonUtils.h"
#include <DNSServer.h> #include <DNSServer.h>
#include <IPAddress.h> #include <IPAddress.h>
@@ -75,7 +75,7 @@ class APSettings {
&& subnetMask == settings.subnetMask; && subnetMask == settings.subnetMask;
} }
static void read(APSettings & settings, JsonObject root); static void read(const APSettings & settings, JsonObject root);
static StateUpdateResult update(JsonObject root, APSettings & settings); static StateUpdateResult update(JsonObject root, APSettings & settings);
}; };

View File

@@ -1,17 +1,15 @@
#include <APStatus.h> #include "APStatus.h"
using namespace std::placeholders; // for `_1` etc
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, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { apStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
} }
void APStatus::apStatus(AsyncWebServerRequest * request) { void APStatus::apStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
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();

View File

@@ -7,8 +7,9 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <IPAddress.h> #include <IPAddress.h>
#include <SecurityManager.h>
#include <APSettingsService.h> #include "SecurityManager.h"
#include "APSettingsService.h"
#define AP_STATUS_SERVICE_PATH "/rest/apStatus" #define AP_STATUS_SERVICE_PATH "/rest/apStatus"

View File

@@ -1,47 +1,27 @@
#include "ArduinoJsonJWT.h" #include "ArduinoJsonJWT.h"
#include <array>
ArduinoJsonJWT::ArduinoJsonJWT(String secret) ArduinoJsonJWT::ArduinoJsonJWT(String secret)
: _secret(secret) { : _secret(std::move(secret)) {
} }
void ArduinoJsonJWT::setSecret(String secret) { void ArduinoJsonJWT::setSecret(String secret) {
_secret = secret; _secret = std::move(secret);
} }
String ArduinoJsonJWT::getSecret() { String ArduinoJsonJWT::getSecret() {
return _secret; return _secret;
} }
/*
* ESP32 uses mbedtls, ESP2866 uses bearssl.
*
* Both come with decent HMAC implementations supporting sha256, as well as others.
*
* No need to pull in additional crypto libraries - lets use what we already have.
*/
String ArduinoJsonJWT::sign(String & payload) {
unsigned char hmacResult[32];
{
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init(&ctx);
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_update(&ctx, (unsigned char *)payload.c_str(), payload.length());
mbedtls_md_hmac_finish(&ctx, hmacResult);
mbedtls_md_free(&ctx);
}
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(), static_cast<int>(jwt.length()));
// add the header to payload // add the header to payload
jwt = JWT_HEADER + '.' + jwt; jwt = getJWTHeader() + '.' + jwt;
// add signature // add signature
jwt += '.' + sign(jwt); jwt += '.' + sign(jwt);
@@ -53,65 +33,88 @@ 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(); jsonDocument.clear();
const String & jwt_header = getJWTHeader();
const unsigned int jwt_header_size = jwt_header.length();
// must have the correct header and delimiter // must have the correct header and delimiter
if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) { if (!jwt.startsWith(jwt_header) || jwt.indexOf('.') != static_cast<int>(jwt_header_size)) {
return; return;
} }
// check there is a signature delimieter // check there is a signature delimieter
int signatureDelimiterIndex = jwt.lastIndexOf('.'); const int signatureDelimiterIndex = jwt.lastIndexOf('.');
if (signatureDelimiterIndex == JWT_HEADER_SIZE) { if (signatureDelimiterIndex == static_cast<int>(jwt_header_size)) {
return; return;
} }
// check the signature is valid // check the signature is valid
String signature = jwt.substring(signatureDelimiterIndex + 1); const String signature = jwt.substring(static_cast<unsigned int>(signatureDelimiterIndex) + 1);
jwt = jwt.substring(0, signatureDelimiterIndex); jwt = jwt.substring(0, static_cast<unsigned int>(signatureDelimiterIndex));
if (sign(jwt) != signature) { if (sign(jwt) != signature) {
return; return;
} }
// decode payload // decode payload
jwt = jwt.substring(JWT_HEADER_SIZE + 1); jwt = jwt.substring(jwt_header_size + 1);
jwt = decode(jwt); jwt = decode(jwt);
// parse payload, clearing json document after failure // parse payload, clearing json document after failure
DeserializationError error = deserializeJson(jsonDocument, jwt); const DeserializationError error = deserializeJson(jsonDocument, jwt);
if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()) { if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()) {
jsonDocument.clear(); jsonDocument.clear();
} }
} }
/*
* ESP32 uses mbedtls, ESP2866 uses bearssl.
*
* Both come with decent HMAC implementations supporting sha256, as well as others.
*
* No need to pull in additional crypto libraries - lets use what we already have.
*/
String ArduinoJsonJWT::sign(String & payload) {
std::array<unsigned char, 32> hmacResult{};
{
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
mbedtls_md_hmac_starts(&ctx, reinterpret_cast<const unsigned char *>(_secret.c_str()), _secret.length());
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char *>(payload.c_str()), payload.length());
mbedtls_md_hmac_finish(&ctx, hmacResult.data());
mbedtls_md_free(&ctx);
}
return encode(reinterpret_cast<const char *>(hmacResult.data()), hmacResult.size());
}
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;
base64_init_encodestate(&_state); base64_init_encodestate(&_state);
size_t encodedLength = base64_encode_expected_len(inputLen) + 1;
// prepare buffer of correct length, returning an empty string on failure // prepare buffer of correct length
char * buffer = (char *)malloc(encodedLength * sizeof(char)); const auto bufferLength = static_cast<std::size_t>(base64_encode_expected_len(inputLen)) + 1;
if (buffer == nullptr) { auto * buffer = new char[bufferLength];
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); auto result = String(buffer);
free(buffer); delete[] 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 (result.length() > 0 && result.charAt(result.length() - 1) == '=') {
value.remove(value.length() - 1); result.remove(result.length() - 1);
} }
value.replace('+', '-'); result.replace('+', '-');
value.replace('/', '_'); result.replace('/', '_');
// return as string // return as string
return value; return result;
} }
String ArduinoJsonJWT::decode(String value) { String ArduinoJsonJWT::decode(String value) {
@@ -120,12 +123,18 @@ String ArduinoJsonJWT::decode(String value) {
value.replace('_', '/'); value.replace('_', '/');
// prepare buffer of correct length // prepare buffer of correct length
char buffer[base64_decode_expected_len(value.length()) + 1]; const auto bufferLength = static_cast<std::size_t>(base64_decode_expected_len(value.length()) + 1);
auto * buffer = new char[bufferLength];
// decode // decode
int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]); const int len = base64_decode_chars(value.c_str(), static_cast<int>(value.length()), &buffer[0]);
buffer[len] = 0; buffer[len] = '\0';
// convert to arduino string, freeing buffer
auto result = String(buffer);
delete[] buffer;
buffer = nullptr;
// return as string // return as string
return String(buffer); return result;
} }

View File

@@ -3,30 +3,33 @@
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <libb64/cdecode.h> #include <libb64/cdecode.h>
#include <libb64/cencode.h> #include <libb64/cencode.h>
#include <mbedtls/md.h> #include <mbedtls/md.h>
class ArduinoJsonJWT { class ArduinoJsonJWT {
private:
String _secret;
const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
const int JWT_HEADER_SIZE = JWT_HEADER.length();
String sign(String & value);
static String encode(const char * cstr, int len);
static String decode(String value);
public: public:
ArduinoJsonJWT(String secret); explicit 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);
private:
String _secret;
String sign(String & value);
static String encode(const char * cstr, int len);
static String decode(String value);
static const String & getJWTHeader() {
static const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
return JWT_HEADER;
}
}; };
#endif #endif

View File

@@ -1,11 +1,9 @@
#include <AuthenticationService.h> #include "AuthenticationService.h"
using namespace std::placeholders; // for `_1` etc
AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager) AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager)
: _securityManager(securityManager) : _securityManager(securityManager)
, _signInHandler(SIGN_IN_PATH, std::bind(&AuthenticationService::signIn, this, _1, _2)) { , _signInHandler(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); }) {
server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, std::bind(&AuthenticationService::verifyAuthorization, this, _1)); server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { verifyAuthorization(request); });
_signInHandler.setMethod(HTTP_POST); _signInHandler.setMethod(HTTP_POST);
_signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE); _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
server->addHandler(&_signInHandler); server->addHandler(&_signInHandler);
@@ -29,10 +27,10 @@ void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant
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); auto * response = new AsyncJsonResponse(false);
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;

View File

@@ -1,9 +1,10 @@
#ifndef AuthenticationService_H_ #ifndef AuthenticationService_H_
#define AuthenticationService_H_ #define AuthenticationService_H_
#include <Features.h> #include "Features.h"
#include "SecurityManager.h"
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization" #define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization"
#define SIGN_IN_PATH "/rest/signIn" #define SIGN_IN_PATH "/rest/signIn"

View File

@@ -1,6 +1,6 @@
#include <ESP8266React.h> #include "ESP8266React.h"
#include <WWWData.h> #include "WWWData.h"
ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs) ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
: _securitySettingsService(server, fs) : _securitySettingsService(server, fs)
@@ -27,7 +27,7 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
static char last_modified[50]; static char last_modified[50];
sprintf(last_modified, "%s %s CET", __DATE__, __TIME__); sprintf(last_modified, "%s %s CET", __DATE__, __TIME__);
WWWData::registerRoutes([server, this](const String & uri, const String & contentType, const uint8_t * content, size_t len, const String & hash) { WWWData::registerRoutes([server](const String & uri, const String & contentType, const uint8_t * content, size_t len, const String & hash) {
ArRequestHandlerFunction requestHandler = [contentType, content, len, hash](AsyncWebServerRequest * request) { ArRequestHandlerFunction requestHandler = [contentType, content, len, hash](AsyncWebServerRequest * request) {
// Check if the client already has the same version and respond with a 304 (Not modified) // Check if the client already has the same version and respond with a 304 (Not modified)
if (request->header("If-Modified-Since").indexOf(last_modified) > 0) { if (request->header("If-Modified-Since").indexOf(last_modified) > 0) {
@@ -66,12 +66,13 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
void ESP8266React::begin() { void ESP8266React::begin() {
_networkSettingsService.begin(); _networkSettingsService.begin();
_networkSettingsService.read([&](NetworkSettings & networkSettings) { _networkSettingsService.read([&](NetworkSettings & networkSettings) {
DefaultHeaders & defaultHeaders = DefaultHeaders::Instance();
if (networkSettings.enableCORS) { if (networkSettings.enableCORS) {
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin); defaultHeaders.addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin);
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); defaultHeaders.addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); defaultHeaders.addHeader("Access-Control-Allow-Credentials", "true");
} }
DefaultHeaders::Instance().addHeader("Server", networkSettings.hostname); defaultHeaders.addHeader("Server", networkSettings.hostname);
}); });
_apSettingsService.begin(); _apSettingsService.begin();
_ntpSettingsService.begin(); _ntpSettingsService.begin();

View File

@@ -1,28 +1,27 @@
#ifndef ESP8266React_h #ifndef ESP8266React_h
#define ESP8266React_h #define ESP8266React_h
#include <Arduino.h> #include "APSettingsService.h"
#include "APStatus.h"
#include "AuthenticationService.h"
#include "FactoryResetService.h"
#include "MqttSettingsService.h"
#include "MqttStatus.h"
#include "NTPSettingsService.h"
#include "NTPStatus.h"
#include "OTASettingsService.h"
#include "UploadFileService.h"
#include "RestartService.h"
#include "SecuritySettingsService.h"
#include "SystemStatus.h"
#include "WiFiScanner.h"
#include "NetworkSettingsService.h"
#include "NetworkStatus.h"
#include <Arduino.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <WiFi.h> #include <WiFi.h>
#include <APSettingsService.h>
#include <APStatus.h>
#include <AuthenticationService.h>
#include <FactoryResetService.h>
#include <MqttSettingsService.h>
#include <MqttStatus.h>
#include <NTPSettingsService.h>
#include <NTPStatus.h>
#include <OTASettingsService.h>
#include <UploadFileService.h>
#include <RestartService.h>
#include <SecuritySettingsService.h>
#include <SystemStatus.h>
#include <WiFiScanner.h>
#include <NetworkSettingsService.h>
#include <NetworkStatus.h>
class ESP8266React { class ESP8266React {
public: public:
ESP8266React(AsyncWebServer * server, FS * fs); ESP8266React(AsyncWebServer * server, FS * fs);

View File

@@ -5,8 +5,8 @@
class ESPUtils { class ESPUtils {
public: public:
static String defaultDeviceValue(String prefix = "") { static String defaultDeviceValue(const String & prefix = "") {
return prefix + String((uint32_t)ESP.getEfuseMac(), HEX); return prefix + String(static_cast<uint32_t>(ESP.getEfuseMac()), HEX);
} }
}; };

View File

@@ -1,8 +1,8 @@
#ifndef FSPersistence_h #ifndef FSPersistence_h
#define FSPersistence_h #define FSPersistence_h
#include <StatefulService.h> #include "StatefulService.h"
#include <FS.h> #include "FS.h"
template <class T> template <class T>
class FSPersistence { class FSPersistence {
@@ -47,8 +47,8 @@ class FSPersistence {
// make directories if required, for new IDF4.2 & LittleFS // make directories if required, for new IDF4.2 & LittleFS
String path(_filePath); String path(_filePath);
int index = 0; int index = 0;
while ((index = path.indexOf('/', index + 1)) != -1) { while ((index = path.indexOf('/', static_cast<unsigned int>(index) + 1)) != -1) {
String segment = path.substring(0, index); String segment = path.substring(0, static_cast<unsigned int>(index));
if (!_fs->exists(segment)) { if (!_fs->exists(segment)) {
_fs->mkdir(segment); _fs->mkdir(segment);
} }
@@ -80,7 +80,7 @@ class FSPersistence {
void enableUpdateHandler() { void enableUpdateHandler() {
if (!_updateHandlerId) { if (!_updateHandlerId) {
_updateHandlerId = _statefulService->addUpdateHandler([&] { writeToFS(); }); _updateHandlerId = _statefulService->addUpdateHandler([this] { writeToFS(); });
} }
} }

View File

@@ -1,16 +1,14 @@
#include <FactoryResetService.h> #include "FactoryResetService.h"
using namespace std::placeholders;
FactoryResetService::FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) FactoryResetService::FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: fs(fs) { : fs(fs) {
server->on(FACTORY_RESET_SERVICE_PATH, server->on(FACTORY_RESET_SERVICE_PATH,
HTTP_POST, HTTP_POST,
securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { handleRequest(request); }, AuthenticationPredicates::IS_ADMIN));
} }
void FactoryResetService::handleRequest(AsyncWebServerRequest * request) { void FactoryResetService::handleRequest(AsyncWebServerRequest * request) {
request->onDisconnect(std::bind(&FactoryResetService::factoryReset, this)); request->onDisconnect([this]() { factoryReset(); });
request->send(200); request->send(200);
} }
@@ -21,7 +19,7 @@ void FactoryResetService::factoryReset() {
// TODO To replaced with fs.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2 // TODO To replaced with fs.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2
File root = fs->open(FS_CONFIG_DIRECTORY); File root = fs->open(FS_CONFIG_DIRECTORY);
File file; File file;
while (file = root.openNextFile()) { while ((file = root.openNextFile())) {
String path = file.path(); String path = file.path();
file.close(); file.close();
fs->remove(path); fs->remove(path);

View File

@@ -3,22 +3,22 @@
#include <WiFi.h> #include <WiFi.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include <RestartService.h>
#include <FS.h> #include <FS.h>
#include "SecurityManager.h"
#include "RestartService.h"
#define FS_CONFIG_DIRECTORY "/config" #define FS_CONFIG_DIRECTORY "/config"
#define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset" #define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset"
class FactoryResetService { class FactoryResetService {
FS * fs;
public: public:
FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager); FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void factoryReset(); void factoryReset();
private: private:
FS * fs;
void handleRequest(AsyncWebServerRequest * request); void handleRequest(AsyncWebServerRequest * request);
}; };

View File

@@ -2,15 +2,13 @@
#define HttpEndpoint_h #define HttpEndpoint_h
#include <functional> #include <functional>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h> #include "SecurityManager.h"
#include <StatefulService.h> #include "StatefulService.h"
#define HTTP_ENDPOINT_ORIGIN_ID "http" #define HTTP_ENDPOINT_ORIGIN_ID "http"
#define HTTPS_ENDPOINT_ORIGIN_ID "https"
using namespace std::placeholders; // for `_1` etc
template <class T> template <class T>
class HttpEndpoint { class HttpEndpoint {
@@ -19,8 +17,7 @@ class HttpEndpoint {
JsonStateUpdater<T> _stateUpdater; JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService; StatefulService<T> * _statefulService;
AsyncCallbackWebHandler * GEThandler; AsyncCallbackJsonWebHandler * handler;
AsyncCallbackJsonWebHandler * POSThandler;
public: public:
HttpEndpoint(JsonStateReader<T> stateReader, HttpEndpoint(JsonStateReader<T> stateReader,
@@ -33,12 +30,12 @@ class HttpEndpoint {
: _stateReader(stateReader) : _stateReader(stateReader)
, _stateUpdater(stateUpdater) , _stateUpdater(stateUpdater)
, _statefulService(statefulService) { , _statefulService(statefulService) {
// Create the GET and POST endpoints // Create hander for both GET and POST endpoints
POSThandler = new AsyncCallbackJsonWebHandler(servicePath, handler = new AsyncCallbackJsonWebHandler(servicePath,
securityManager->wrapCallback([this](AsyncWebServerRequest * request, securityManager->wrapCallback([this](AsyncWebServerRequest * request,
JsonVariant json) { handleRequest(request, json); }, JsonVariant json) { handleRequest(request, json); },
authenticationPredicate)); authenticationPredicate));
server->addHandler(POSThandler); server->addHandler(handler);
} }
protected: protected:
@@ -66,8 +63,8 @@ class HttpEndpoint {
} }
} }
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
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);

View File

@@ -3,16 +3,20 @@
#include <IPAddress.h> #include <IPAddress.h>
const IPAddress IP_NOT_SET = IPAddress(INADDR_NONE);
class IPUtils { class IPUtils {
public: public:
static bool isSet(const IPAddress & ip) { static bool isSet(const IPAddress & ip) {
return ip != IP_NOT_SET; return ip != getNotSetIP();
} }
static bool isNotSet(const IPAddress & ip) { static bool isNotSet(const IPAddress & ip) {
return ip == IP_NOT_SET; return ip == getNotSetIP();
}
private:
static const IPAddress & getNotSetIP() {
static const IPAddress IP_NOT_SET = IPAddress(INADDR_NONE);
return IP_NOT_SET;
} }
}; };
#endif #endif

View File

@@ -2,9 +2,10 @@
#define JsonUtils_h #define JsonUtils_h
#include <Arduino.h> #include <Arduino.h>
#include <IPAddress.h>
#include <IPUtils.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <IPAddress.h>
#include "IPUtils.h"
class JsonUtils { class JsonUtils {
public: public:

View File

@@ -1,22 +1,23 @@
#include <MqttSettingsService.h> #include "MqttSettingsService.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
/** /**
* Retains a copy of the cstr provided in the pointer provided using dynamic allocation. * Retains a copy of the cstr provided in the pointer provided using dynamic allocation.
* *
* Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr. * Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr.
*/ */
static char * retainCstr(const char * cstr, char ** ptr) { static char * retainCstr(const char * cstr, char ** ptr) {
if (ptr == nullptr || *ptr == nullptr) {
return nullptr;
}
// free up previously retained value if exists // free up previously retained value if exists
free(*ptr); delete[] *ptr;
*ptr = nullptr; *ptr = nullptr;
// dynamically allocate and copy cstr (if non null) // dynamically allocate and copy cstr (if non null)
if (cstr != nullptr) { if (cstr != nullptr) {
*ptr = (char *)malloc(strlen(cstr) + 1); *ptr = new char[strlen(cstr) + 1];
strcpy(*ptr, cstr); strcpy(*ptr, cstr);
} }
@@ -31,15 +32,22 @@ MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, Secur
, _retainedClientId(nullptr) , _retainedClientId(nullptr)
, _retainedUsername(nullptr) , _retainedUsername(nullptr)
, _retainedPassword(nullptr) , _retainedPassword(nullptr)
, _retainedRootCA(nullptr)
, _reconfigureMqtt(false) , _reconfigureMqtt(false)
, _disconnectedAt(0) , _disconnectedAt(0)
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED) , _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
, _mqttClient(nullptr) { , _mqttClient(nullptr) {
WiFi.onEvent(std::bind(&MqttSettingsService::WiFiEvent, this, _1, _2)); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
addUpdateHandler([&] { onConfigUpdated(); }, false); addUpdateHandler([this]() { onConfigUpdated(); }, false);
} }
MqttSettingsService::~MqttSettingsService() { MqttSettingsService::~MqttSettingsService() {
delete _mqttClient;
retainCstr(nullptr, &_retainedHost);
retainCstr(nullptr, &_retainedClientId);
retainCstr(nullptr, &_retainedUsername);
retainCstr(nullptr, &_retainedPassword);
retainCstr(nullptr, &_retainedRootCA);
} }
void MqttSettingsService::begin() { void MqttSettingsService::begin() {
@@ -55,32 +63,41 @@ void MqttSettingsService::startClient() {
return; return;
} }
delete _mqttClient; delete _mqttClient;
_mqttClient = nullptr;
} }
#if CONFIG_IDF_TARGET_ESP32S3 #if CONFIG_IDF_TARGET_ESP32S3
if (_state.enableTLS) { if (_state.enableTLS) {
isSecure = true; isSecure = true;
_mqttClient = static_cast<MqttClient *>(new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO)); _mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
if (_state.rootCA == "insecure") { if (_state.rootCA == "insecure") {
static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure(); static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure();
} else { } else {
String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n"; String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n";
static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(retainCstr(certificate.c_str(), &_retainedRootCA)); static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(retainCstr(certificate.c_str(), &_retainedRootCA));
} }
static_cast<espMqttClientSecure *>(_mqttClient)->onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, _1)); static_cast<espMqttClientSecure *>(_mqttClient)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
static_cast<espMqttClientSecure *>(_mqttClient)->onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, _1)); static_cast<espMqttClientSecure *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
static_cast<espMqttClientSecure *>(_mqttClient)->onMessage(std::bind(&MqttSettingsService::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); static_cast<espMqttClientSecure *>(_mqttClient)
->onMessage(
[this](const espMqttClientTypes::MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total) {
onMqttMessage(properties, topic, payload, len, index, total);
});
return; return;
} }
#endif #endif
isSecure = false; isSecure = false;
_mqttClient = static_cast<MqttClient *>(new espMqttClient(espMqttClientTypes::UseInternalTask::NO)); _mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);
static_cast<espMqttClient *>(_mqttClient)->onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, _1)); static_cast<espMqttClient *>(_mqttClient)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
static_cast<espMqttClient *>(_mqttClient)->onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, _1)); static_cast<espMqttClient *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
static_cast<espMqttClient *>(_mqttClient)->onMessage(std::bind(&MqttSettingsService::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); static_cast<espMqttClient *>(_mqttClient)
->onMessage(
[this](const espMqttClientTypes::MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total) {
onMqttMessage(properties, topic, payload, len, index, total);
});
} }
void MqttSettingsService::loop() { void MqttSettingsService::loop() {
if (_reconfigureMqtt || (_disconnectedAt && (uint32_t)(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) { if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
// reconfigure MQTT client // reconfigure MQTT client
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime(); _disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
_reconfigureMqtt = false; _reconfigureMqtt = false;
@@ -116,6 +133,9 @@ void MqttSettingsService::onMqttMessage(const espMqttClientTypes::MessagePropert
size_t len, size_t len,
size_t index, size_t index,
size_t total) { size_t total) {
(void)properties;
(void)index;
(void)total;
emsesp::EMSESP::mqtt_.on_message(topic, payload, len); emsesp::EMSESP::mqtt_.on_message(topic, payload, len);
} }
@@ -128,6 +148,7 @@ MqttClient * MqttSettingsService::getMqttClient() {
} }
void MqttSettingsService::onMqttConnect(bool sessionPresent) { void MqttSettingsService::onMqttConnect(bool sessionPresent) {
(void)sessionPresent;
// _disconnectedAt = 0; // _disconnectedAt = 0;
emsesp::EMSESP::mqtt_.on_connect(); emsesp::EMSESP::mqtt_.on_connect();
// emsesp::EMSESP::logger().info("Connected to MQTT, %s", (sessionPresent) ? ("with persistent session") : ("without persistent session")); // emsesp::EMSESP::logger().info("Connected to MQTT, %s", (sessionPresent) ? ("with persistent session") : ("without persistent session"));
@@ -149,7 +170,7 @@ void MqttSettingsService::onConfigUpdated() {
emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings
} }
void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { void MqttSettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) { switch (event) {
case ARDUINO_EVENT_WIFI_STA_GOT_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP: case ARDUINO_EVENT_ETH_GOT_IP:
@@ -264,32 +285,32 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
#endif #endif
newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED;
newSettings.host = root["host"] | FACTORY_MQTT_HOST; newSettings.host = root["host"] | FACTORY_MQTT_HOST;
newSettings.port = root["port"] | FACTORY_MQTT_PORT; newSettings.port = static_cast<uint16_t>(root["port"] | FACTORY_MQTT_PORT);
newSettings.base = root["base"] | FACTORY_MQTT_BASE; newSettings.base = root["base"] | FACTORY_MQTT_BASE;
newSettings.username = root["username"] | FACTORY_MQTT_USERNAME; newSettings.username = root["username"] | FACTORY_MQTT_USERNAME;
newSettings.password = root["password"] | FACTORY_MQTT_PASSWORD; newSettings.password = root["password"] | FACTORY_MQTT_PASSWORD;
newSettings.clientId = root["client_id"] | FACTORY_MQTT_CLIENT_ID; newSettings.clientId = root["client_id"] | FACTORY_MQTT_CLIENT_ID;
newSettings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE; newSettings.keepAlive = static_cast<uint16_t>(root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE);
newSettings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION; newSettings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION;
newSettings.mqtt_qos = root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS; newSettings.mqtt_qos = static_cast<uint8_t>(root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS);
newSettings.mqtt_retain = root["mqtt_retain"] | EMSESP_DEFAULT_MQTT_RETAIN; newSettings.mqtt_retain = root["mqtt_retain"] | EMSESP_DEFAULT_MQTT_RETAIN;
newSettings.publish_time_boiler = root["publish_time_boiler"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_boiler = static_cast<uint16_t>(root["publish_time_boiler"] | EMSESP_DEFAULT_PUBLISH_TIME);
newSettings.publish_time_thermostat = root["publish_time_thermostat"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_thermostat = static_cast<uint16_t>(root["publish_time_thermostat"] | EMSESP_DEFAULT_PUBLISH_TIME);
newSettings.publish_time_solar = root["publish_time_solar"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_solar = static_cast<uint16_t>(root["publish_time_solar"] | EMSESP_DEFAULT_PUBLISH_TIME);
newSettings.publish_time_mixer = root["publish_time_mixer"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_mixer = static_cast<uint16_t>(root["publish_time_mixer"] | EMSESP_DEFAULT_PUBLISH_TIME);
newSettings.publish_time_other = root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_other = static_cast<uint16_t>(root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME);
newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_sensor = static_cast<uint16_t>(root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME);
newSettings.publish_time_heartbeat = root["publish_time_heartbeat"] | EMSESP_DEFAULT_PUBLISH_HEARTBEAT; newSettings.publish_time_heartbeat = static_cast<uint16_t>(root["publish_time_heartbeat"] | EMSESP_DEFAULT_PUBLISH_HEARTBEAT);
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED; newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT; newSettings.nested_format = static_cast<uint8_t>(root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT);
newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX; newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX;
newSettings.discovery_type = root["discovery_type"] | EMSESP_DEFAULT_DISCOVERY_TYPE; newSettings.discovery_type = static_cast<uint8_t>(root["discovery_type"] | EMSESP_DEFAULT_DISCOVERY_TYPE);
newSettings.publish_single = root["publish_single"] | EMSESP_DEFAULT_PUBLISH_SINGLE; newSettings.publish_single = root["publish_single"] | EMSESP_DEFAULT_PUBLISH_SINGLE;
newSettings.publish_single2cmd = root["publish_single2cmd"] | EMSESP_DEFAULT_PUBLISH_SINGLE2CMD; newSettings.publish_single2cmd = root["publish_single2cmd"] | EMSESP_DEFAULT_PUBLISH_SINGLE2CMD;
newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE; newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
newSettings.entity_format = root["entity_format"] | EMSESP_DEFAULT_ENTITY_FORMAT; newSettings.entity_format = static_cast<uint8_t>(root["entity_format"] | EMSESP_DEFAULT_ENTITY_FORMAT);
if (newSettings.enabled != settings.enabled) { if (newSettings.enabled != settings.enabled) {
changed = true; changed = true;

View File

@@ -1,11 +1,12 @@
#ifndef MqttSettingsService_h #ifndef MqttSettingsService_h
#define MqttSettingsService_h #define MqttSettingsService_h
#include <StatefulService.h> #include "StatefulService.h"
#include <HttpEndpoint.h> #include "HttpEndpoint.h"
#include <FSPersistence.h> #include "FSPersistence.h"
#include "ESPUtils.h"
#include <espMqttClient.h> #include <espMqttClient.h>
#include <ESPUtils.h>
#include <uuid/common.h> #include <uuid/common.h>
@@ -141,11 +142,12 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
// the MQTT client instance // the MQTT client instance
MqttClient * _mqttClient; MqttClient * _mqttClient;
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info); void WiFiEvent(WiFiEvent_t event);
void onMqttConnect(bool sessionPresent); void onMqttConnect(bool sessionPresent);
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason); void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
void void
onMqttMessage(const espMqttClientTypes::MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total); onMqttMessage(const espMqttClientTypes::MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total);
bool configureMqtt(); bool configureMqtt();
}; };

View File

@@ -1,19 +1,17 @@
#include <MqttStatus.h> #include "MqttStatus.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
MqttStatus::MqttStatus(AsyncWebServer * server, MqttSettingsService * mqttSettingsService, SecurityManager * securityManager) MqttStatus::MqttStatus(AsyncWebServer * server, MqttSettingsService * mqttSettingsService, SecurityManager * securityManager)
: _mqttSettingsService(mqttSettingsService) { : _mqttSettingsService(mqttSettingsService) {
server->on(MQTT_STATUS_SERVICE_PATH, server->on(MQTT_STATUS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&MqttStatus::mqttStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { mqttStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
} }
void MqttStatus::mqttStatus(AsyncWebServerRequest * request) { void MqttStatus::mqttStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["enabled"] = _mqttSettingsService->isEnabled(); root["enabled"] = _mqttSettingsService->isEnabled();
root["connected"] = _mqttSettingsService->isConnected(); root["connected"] = _mqttSettingsService->isConnected();

View File

@@ -2,10 +2,11 @@
#define MqttStatus_h #define MqttStatus_h
#include <WiFi.h> #include <WiFi.h>
#include <MqttSettingsService.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include "MqttSettingsService.h"
#include "SecurityManager.h"
#define MQTT_STATUS_SERVICE_PATH "/rest/mqttStatus" #define MQTT_STATUS_SERVICE_PATH "/rest/mqttStatus"

View File

@@ -1,20 +1,20 @@
#include <NTPSettingsService.h> #include "NTPSettingsService.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
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, securityManager->wrapCallback(std::bind(&NTPSettingsService::configureTime, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { , _timeHandler(TIME_PATH,
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { configureTime(request, json); },
AuthenticationPredicates::IS_ADMIN))
, _connected(false) {
_timeHandler.setMethod(HTTP_POST); _timeHandler.setMethod(HTTP_POST);
_timeHandler.setMaxContentLength(MAX_TIME_SIZE); _timeHandler.setMaxContentLength(MAX_TIME_SIZE);
server->addHandler(&_timeHandler); server->addHandler(&_timeHandler);
WiFi.onEvent(std::bind(&NTPSettingsService::WiFiEvent, this, _1)); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
addUpdateHandler([this] { configureNTP(); }, false);
addUpdateHandler([&] { configureNTP(); }, false);
} }
void NTPSettingsService::begin() { void NTPSettingsService::begin() {
@@ -27,9 +27,9 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) { switch (event) {
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED: case ARDUINO_EVENT_ETH_DISCONNECTED:
if (connected_) { if (_connected) {
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP"); emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
connected_ = false; _connected = false;
configureNTP(); configureNTP();
} }
break; break;
@@ -37,7 +37,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
case ARDUINO_EVENT_WIFI_STA_GOT_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP: case ARDUINO_EVENT_ETH_GOT_IP:
// emsesp::EMSESP::logger().info("Got IP address, starting NTP synchronization"); // emsesp::EMSESP::logger().info("Got IP address, starting NTP synchronization");
connected_ = true; _connected = true;
configureNTP(); configureNTP();
break; break;
@@ -49,7 +49,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
// https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm // https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
void NTPSettingsService::configureNTP() { void NTPSettingsService::configureNTP() {
emsesp::EMSESP::system_.ntp_connected(false); emsesp::EMSESP::system_.ntp_connected(false);
if (connected_ && _state.enabled) { if (_connected && _state.enabled) {
emsesp::EMSESP::logger().info("Starting NTP service"); emsesp::EMSESP::logger().info("Starting NTP service");
esp_sntp_set_sync_interval(3600000); // one hour esp_sntp_set_sync_interval(3600000); // one hour
esp_sntp_set_time_sync_notification_cb(ntp_received); esp_sntp_set_time_sync_notification_cb(ntp_received);
@@ -63,13 +63,13 @@ void NTPSettingsService::configureNTP() {
void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant json) { void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
struct tm tm = {0}; struct tm tm = {};
String timeLocal = json["local_time"]; String timeLocal = json["local_time"];
char * s = strptime(timeLocal.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); char * s = strptime(timeLocal.c_str(), "%Y-%m-%dT%H:%M:%S", &tm);
if (s != nullptr) { if (s != nullptr) {
tm.tm_isdst = -1; // not set by strptime, tells mktime to determine daylightsaving tm.tm_isdst = -1; // not set by strptime, tells mktime to determine daylightsaving
time_t time = mktime(&tm); time_t time = mktime(&tm);
struct timeval now = {.tv_sec = time}; struct timeval now = {.tv_sec = time, .tv_usec = {}};
settimeofday(&now, nullptr); settimeofday(&now, nullptr);
AsyncWebServerResponse * response = request->beginResponse(200); AsyncWebServerResponse * response = request->beginResponse(200);
request->send(response); request->send(response);
@@ -82,6 +82,7 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
} }
void NTPSettingsService::ntp_received(struct timeval * tv) { void NTPSettingsService::ntp_received(struct timeval * tv) {
(void)tv;
// emsesp::EMSESP::logger().info("NTP sync to %d sec", tv->tv_sec); // emsesp::EMSESP::logger().info("NTP sync to %d sec", tv->tv_sec);
emsesp::EMSESP::system_.ntp_connected(true); emsesp::EMSESP::system_.ntp_connected(true);
} }
@@ -99,4 +100,4 @@ StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
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;
} }

View File

@@ -1,10 +1,10 @@
#ifndef NTPSettingsService_h #ifndef NTPSettingsService_h
#define NTPSettingsService_h #define NTPSettingsService_h
#include <HttpEndpoint.h> #include "HttpEndpoint.h"
#include <FSPersistence.h> #include "FSPersistence.h"
#include <time.h> #include <ctime>
#include <esp_sntp.h> #include <esp_sntp.h>
#ifndef FACTORY_NTP_ENABLED #ifndef FACTORY_NTP_ENABLED
@@ -51,8 +51,8 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
HttpEndpoint<NTPSettings> _httpEndpoint; HttpEndpoint<NTPSettings> _httpEndpoint;
FSPersistence<NTPSettings> _fsPersistence; FSPersistence<NTPSettings> _fsPersistence;
AsyncCallbackJsonWebHandler _timeHandler; AsyncCallbackJsonWebHandler _timeHandler;
bool _connected;
bool connected_ = false;
void WiFiEvent(WiFiEvent_t event); void WiFiEvent(WiFiEvent_t event);
void configureNTP(); void configureNTP();
void configureTime(AsyncWebServerRequest * request, JsonVariant json); void configureTime(AsyncWebServerRequest * request, JsonVariant json);

View File

@@ -1,12 +1,13 @@
#include <NTPStatus.h> #include "NTPStatus.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc #include <array>
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, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { ntpStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
} }
/* /*
@@ -15,9 +16,9 @@ NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager)
* Uses a 25 byte buffer, large enough to fit an ISO time string with offset. * Uses a 25 byte buffer, large enough to fit an ISO time string with offset.
*/ */
String formatTime(tm * time, const char * format) { String formatTime(tm * time, const char * format) {
char time_string[25]; std::array<char, 25> time_string{};
strftime(time_string, 25, format, time); strftime(time_string.data(), time_string.size(), format, time);
return String(time_string); return {time_string.data()};
} }
String toUTCTimeString(tm * time) { String toUTCTimeString(tm * time) {
@@ -29,14 +30,23 @@ String toLocalTimeString(tm * time) {
} }
void NTPStatus::ntpStatus(AsyncWebServerRequest * request) { void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
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"] = esp_sntp_enabled() ? emsesp::EMSESP::system_.ntp_connected() ? 2 : 1 : 0; root["status"] = []() {
if (esp_sntp_enabled()) {
if (emsesp::EMSESP::system_.ntp_connected()) {
return 2;
} else {
return 1;
}
}
return 0;
}();
// the current time in UTC // the current time in UTC
root["utc_time"] = toUTCTimeString(gmtime(&now)); root["utc_time"] = toUTCTimeString(gmtime(&now));
@@ -49,4 +59,4 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@@ -1,14 +1,14 @@
#ifndef NTPStatus_h #ifndef NTPStatus_h
#define NTPStatus_h #define NTPStatus_h
#include <time.h> #include <ctime>
#include <WiFi.h> #include <WiFi.h>
#include <esp_sntp.h> #include <esp_sntp.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include "SecurityManager.h"
#include <uuid/common.h> #include <uuid/common.h>
#define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus" #define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus"

View File

@@ -1,16 +1,25 @@
#include <NetworkSettingsService.h> #include "NetworkSettingsService.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(NetworkSettings::read, NetworkSettings::update, this, server, NETWORK_SETTINGS_SERVICE_PATH, securityManager) : _httpEndpoint(NetworkSettings::read, NetworkSettings::update, this, server, NETWORK_SETTINGS_SERVICE_PATH, securityManager)
, _fsPersistence(NetworkSettings::read, NetworkSettings::update, this, fs, NETWORK_SETTINGS_FILE) , _fsPersistence(NetworkSettings::read, NetworkSettings::update, this, fs, NETWORK_SETTINGS_FILE)
, _lastConnectionAttempt(0) { , _lastConnectionAttempt(0)
addUpdateHandler([&] { reconfigureWiFiConnection(); }, false); , _stopping(false) {
// wifi event callbacks addUpdateHandler([this]() { reconfigureWiFiConnection(); }, false);
WiFi.onEvent(std::bind(&NetworkSettingsService::WiFiEvent, this, _1, _2)); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event, info); });
}
static bool formatBssid(const String & bssid, uint8_t (&mac)[6]) {
uint tmp[6];
if (bssid.isEmpty() || sscanf(bssid.c_str(), "%X:%X:%X:%X:%X:%X", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) != 6) {
return false;
}
for (uint8_t i = 0; i < 6; i++) {
mac[i] = static_cast<uint8_t>(tmp[i]);
}
return true;
} }
void NetworkSettingsService::begin() { void NetworkSettingsService::begin() {
@@ -47,7 +56,7 @@ void NetworkSettingsService::reconfigureWiFiConnection() {
void NetworkSettingsService::loop() { void NetworkSettingsService::loop() {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
if (!_lastConnectionAttempt || (uint32_t)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) { if (!_lastConnectionAttempt || static_cast<uint32_t>(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) {
_lastConnectionAttempt = currentMillis; _lastConnectionAttempt = currentMillis;
manageSTA(); manageSTA();
} }
@@ -68,22 +77,18 @@ void NetworkSettingsService::manageSTA() {
// www.esp32.com/viewtopic.php?t=12055 // www.esp32.com/viewtopic.php?t=12055
if (_state.bandwidth20) { if (_state.bandwidth20) {
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_STA, WIFI_BW_HT20); esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
} else { } else {
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_STA, WIFI_BW_HT40); esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
} }
if (_state.nosleep) { if (_state.nosleep) {
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
} }
// attempt to connect to the network // attempt to connect to the network
uint mac[6]; uint8_t bssid[6];
if (!_state.bssid.isEmpty() && sscanf(_state.bssid.c_str(), "%X:%X:%X:%X:%X:%X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) { if (formatBssid(_state.bssid, bssid)) {
uint8_t mac1[6]; WiFi.begin(_state.ssid.c_str(), _state.password.c_str(), 0, bssid);
for (uint8_t i = 0; i < 6; i++) {
mac1[i] = (uint8_t)mac[i];
}
WiFi.begin(_state.ssid.c_str(), _state.password.c_str(), 0, mac1);
} else { } else {
WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); WiFi.begin(_state.ssid.c_str(), _state.password.c_str());
} }
@@ -96,7 +101,7 @@ void NetworkSettingsService::manageSTA() {
#else #else
if (_state.tx_power != 0) { if (_state.tx_power != 0) {
// if not set to Auto (0) set the Tx power now // if not set to Auto (0) set the Tx power now
if (!WiFi.setTxPower((wifi_power_t)_state.tx_power)) { if (!WiFi.setTxPower(static_cast<wifi_power_t>(_state.tx_power))) {
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power"); emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
} }
} }
@@ -173,7 +178,7 @@ void NetworkSettingsService::setWiFiPowerOnRSSI() {
emsesp::EMSESP::logger().debug("Recommended set WiFi Tx Power (set_power %d, new power %d, rssi %d, threshold %d", set_power, p, rssi, threshold); emsesp::EMSESP::logger().debug("Recommended set WiFi Tx Power (set_power %d, new power %d, rssi %d, threshold %d", set_power, p, rssi, threshold);
#else #else
char result[10]; char result[10];
emsesp::EMSESP::logger().info("Setting WiFi Tx Power to %s dBm", emsesp::Helpers::render_value(result, (double)(p / 4), 1)); emsesp::EMSESP::logger().info("Setting WiFi Tx Power to %s dBm", emsesp::Helpers::render_value(result, ((double)(p) / 4), 1));
#endif #endif
if (!WiFi.setTxPower(p)) { if (!WiFi.setTxPower(p)) {
@@ -310,7 +315,7 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
emsesp::EMSESP::logger().info("WiFi connected with IP=%s, hostname=%s, TxPower=%s dBm", emsesp::EMSESP::logger().info("WiFi connected with IP=%s, hostname=%s, TxPower=%s dBm",
WiFi.localIP().toString().c_str(), WiFi.localIP().toString().c_str(),
WiFi.getHostname(), WiFi.getHostname(),
emsesp::Helpers::render_value(result, (double)(WiFi.getTxPower() / 4), 1)); emsesp::Helpers::render_value(result, ((double)(WiFi.getTxPower()) / 4), 1));
mDNS_start(); mDNS_start();
break; break;
@@ -389,7 +394,6 @@ void NetworkSettings::read(NetworkSettings & settings, JsonObject root) {
root["static_ip_config"] = settings.staticIPConfig; root["static_ip_config"] = settings.staticIPConfig;
root["enableIPv6"] = settings.enableIPv6; root["enableIPv6"] = settings.enableIPv6;
root["bandwidth20"] = settings.bandwidth20; root["bandwidth20"] = settings.bandwidth20;
root["tx_power"] = settings.tx_power;
root["nosleep"] = settings.nosleep; root["nosleep"] = settings.nosleep;
root["enableMDNS"] = settings.enableMDNS; root["enableMDNS"] = settings.enableMDNS;
root["enableCORS"] = settings.enableCORS; root["enableCORS"] = settings.enableCORS;
@@ -417,7 +421,7 @@ StateUpdateResult NetworkSettings::update(JsonObject root, NetworkSettings & set
settings.staticIPConfig = root["static_ip_config"] | false; settings.staticIPConfig = root["static_ip_config"] | false;
settings.enableIPv6 = root["enableIPv6"] | false; settings.enableIPv6 = root["enableIPv6"] | false;
settings.bandwidth20 = root["bandwidth20"] | false; settings.bandwidth20 = root["bandwidth20"] | false;
settings.tx_power = root["tx_power"] | 0; settings.tx_power = static_cast<uint8_t>(root["tx_power"] | 0);
settings.nosleep = root["nosleep"] | false; settings.nosleep = root["nosleep"] | false;
settings.enableMDNS = root["enableMDNS"] | true; settings.enableMDNS = root["enableMDNS"] | true;
settings.enableCORS = root["enableCORS"] | false; settings.enableCORS = root["enableCORS"] | false;
@@ -445,7 +449,7 @@ StateUpdateResult NetworkSettings::update(JsonObject root, NetworkSettings & set
// see if we need to inform the user of a restart // see if we need to inform the user of a restart
if (tx_power != settings.tx_power || enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin if (tx_power != settings.tx_power || enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin
|| (ssid != settings.ssid && settings.ssid == "")) { || (ssid != settings.ssid && settings.ssid.isEmpty())) {
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
} }

View File

@@ -1,10 +1,10 @@
#ifndef NetworkSettingsService_h #ifndef NetworkSettingsService_h
#define NetworkSettingsService_h #define NetworkSettingsService_h
#include <StatefulService.h> #include "StatefulService.h"
#include <FSPersistence.h> #include "FSPersistence.h"
#include <HttpEndpoint.h> #include "HttpEndpoint.h"
#include <JsonUtils.h> #include "JsonUtils.h"
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
#include <esp_wifi.h> #include <esp_wifi.h>
@@ -16,7 +16,7 @@
#define NETWORK_SETTINGS_FILE "/config/networkSettings.json" #define NETWORK_SETTINGS_FILE "/config/networkSettings.json"
#define NETWORK_SETTINGS_SERVICE_PATH "/rest/networkSettings" #define NETWORK_SETTINGS_SERVICE_PATH "/rest/networkSettings"
#define WIFI_RECONNECTION_DELAY 1000 * 3 #define WIFI_RECONNECTION_DELAY (1000 * 3)
#ifndef FACTORY_WIFI_SSID #ifndef FACTORY_WIFI_SSID
#define FACTORY_WIFI_SSID "" #define FACTORY_WIFI_SSID ""

View File

@@ -1,18 +1,16 @@
#include <NetworkStatus.h> #include "NetworkStatus.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager) { NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(NETWORK_STATUS_SERVICE_PATH, server->on(NETWORK_STATUS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { networkStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
} }
void NetworkStatus::networkStatus(AsyncWebServerRequest * request) { void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
bool ethernet_connected = emsesp::EMSESP::system_.ethernet_connected(); bool ethernet_connected = emsesp::EMSESP::system_.ethernet_connected();
wl_status_t wifi_status = WiFi.status(); wl_status_t wifi_status = WiFi.status();
@@ -22,7 +20,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
root["status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED root["status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED
root["hostname"] = ETH.getHostname(); root["hostname"] = ETH.getHostname();
} else { } else {
root["status"] = (uint8_t)wifi_status; root["status"] = static_cast<uint8_t>(wifi_status);
root["hostname"] = WiFi.getHostname(); root["hostname"] = WiFi.getHostname();
} }

View File

@@ -4,9 +4,9 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <IPAddress.h> #include <IPAddress.h>
#include <IPUtils.h>
#include <SecurityManager.h> #include "IPUtils.h"
#include "SecurityManager.h"
#define MAX_NETWORK_STATUS_SIZE 1024 #define MAX_NETWORK_STATUS_SIZE 1024
#define NETWORK_STATUS_SERVICE_PATH "/rest/networkStatus" #define NETWORK_STATUS_SERVICE_PATH "/rest/networkStatus"

View File

@@ -1,15 +1,13 @@
#include <OTASettingsService.h> #include "OTASettingsService.h"
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
OTASettingsService::OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) OTASettingsService::OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager) : _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager)
, _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE) , _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE)
, _arduinoOTA(nullptr) { , _arduinoOTA(nullptr) {
WiFi.onEvent(std::bind(&OTASettingsService::WiFiEvent, this, _1, _2)); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
addUpdateHandler([&] { configureArduinoOTA(); }, false); addUpdateHandler([this] { configureArduinoOTA(); }, false);
} }
void OTASettingsService::begin() { void OTASettingsService::begin() {
@@ -32,7 +30,7 @@ void OTASettingsService::configureArduinoOTA() {
if (_state.enabled) { if (_state.enabled) {
_arduinoOTA = new ArduinoOTAClass; _arduinoOTA = new ArduinoOTAClass;
_arduinoOTA->setPort(_state.port); _arduinoOTA->setPort(static_cast<uint16_t>(_state.port));
_arduinoOTA->setPassword(_state.password.c_str()); _arduinoOTA->setPassword(_state.password.c_str());
_arduinoOTA->onStart([] { emsesp::EMSESP::system_.upload_status(true); }); _arduinoOTA->onStart([] { emsesp::EMSESP::system_.upload_status(true); });
@@ -64,7 +62,7 @@ void OTASettingsService::configureArduinoOTA() {
} }
} }
void OTASettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { void OTASettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) { switch (event) {
case ARDUINO_EVENT_WIFI_STA_GOT_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP: case ARDUINO_EVENT_ETH_GOT_IP:

View File

@@ -1,8 +1,8 @@
#ifndef OTASettingsService_h #ifndef OTASettingsService_h
#define OTASettingsService_h #define OTASettingsService_h
#include <HttpEndpoint.h> #include "HttpEndpoint.h"
#include <FSPersistence.h> #include "FSPersistence.h"
#include <ArduinoOTA.h> #include <ArduinoOTA.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
@@ -45,7 +45,7 @@ class OTASettingsService : public StatefulService<OTASettings> {
ArduinoOTAClass * _arduinoOTA; ArduinoOTAClass * _arduinoOTA;
void configureArduinoOTA(); void configureArduinoOTA();
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info); void WiFiEvent(WiFiEvent_t event);
}; };
#endif #endif

View File

@@ -1,15 +1,22 @@
#include <RestartService.h> #include "RestartService.h"
#include <esp_ota_ops.h> #include <esp_ota_ops.h>
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
RestartService::RestartService(AsyncWebServer * server, SecurityManager * securityManager) { RestartService::RestartService(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(RESTART_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest(std::bind(&RestartService::restart, this, _1), AuthenticationPredicates::IS_ADMIN)); server->on(RESTART_SERVICE_PATH,
HTTP_POST,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { restart(request); }, AuthenticationPredicates::IS_ADMIN));
server->on(PARTITION_SERVICE_PATH, server->on(PARTITION_SERVICE_PATH,
HTTP_POST, HTTP_POST,
securityManager->wrapRequest(std::bind(&RestartService::partition, this, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { partition(request); }, AuthenticationPredicates::IS_ADMIN));
}
void RestartService::restartNow() {
WiFi.disconnect(true);
delay(500);
ESP.restart();
} }
void RestartService::restart(AsyncWebServerRequest * request) { void RestartService::restart(AsyncWebServerRequest * request) {
@@ -19,7 +26,7 @@ void RestartService::restart(AsyncWebServerRequest * request) {
} }
void RestartService::partition(AsyncWebServerRequest * request) { void RestartService::partition(AsyncWebServerRequest * request) {
const esp_partition_t * factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); const esp_partition_t * factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr);
if (factory_partition) { if (factory_partition) {
esp_ota_set_boot_partition(factory_partition); esp_ota_set_boot_partition(factory_partition);
emsesp::EMSESP::system_.store_nvs_values(); emsesp::EMSESP::system_.store_nvs_values();
@@ -27,7 +34,7 @@ void RestartService::partition(AsyncWebServerRequest * request) {
request->send(200); request->send(200);
return; return;
} }
const esp_partition_t * ota_partition = esp_ota_get_next_update_partition(NULL); const esp_partition_t * ota_partition = esp_ota_get_next_update_partition(nullptr);
if (!ota_partition) { if (!ota_partition) {
request->send(400); // bad request request->send(400); // bad request
return; return;

View File

@@ -3,9 +3,9 @@
#include <WiFi.h> #include <WiFi.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include "SecurityManager.h"
#define RESTART_SERVICE_PATH "/rest/restart" #define RESTART_SERVICE_PATH "/rest/restart"
#define PARTITION_SERVICE_PATH "/rest/partition" #define PARTITION_SERVICE_PATH "/rest/partition"
@@ -14,11 +14,7 @@ class RestartService {
public: public:
RestartService(AsyncWebServer * server, SecurityManager * securityManager); RestartService(AsyncWebServer * server, SecurityManager * securityManager);
static void restartNow() { static void restartNow();
WiFi.disconnect(true);
delay(500);
ESP.restart();
}
private: private:
void restart(AsyncWebServerRequest * request); void restart(AsyncWebServerRequest * request);

View File

@@ -1,10 +1,11 @@
#ifndef SecurityManager_h #ifndef SecurityManager_h
#define SecurityManager_h #define SecurityManager_h
#include <Features.h> #include "Features.h"
#include <ArduinoJsonJWT.h> #include "ArduinoJsonJWT.h"
#include "ESPUtils.h"
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <ESPUtils.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <list> #include <list>
@@ -26,28 +27,27 @@ class User {
public: public:
User(String username, String password, bool admin) User(String username, String password, bool admin)
: username(username) : username(std::move(username))
, password(password) , password(std::move(password))
, admin(admin) { , admin(admin) {
} }
}; };
class Authentication { class Authentication {
public: public:
User * user; User * user = nullptr;
boolean authenticated; boolean authenticated = false;
public: public:
Authentication(User & user) explicit Authentication(const User & user)
: user(new User(user)) : user(new User(user))
, authenticated(true) { , authenticated(true) {
} }
Authentication()
: user(nullptr) Authentication() = default;
, authenticated(false) {
}
~Authentication() { ~Authentication() {
delete (user); delete user;
} }
}; };
@@ -55,13 +55,14 @@ typedef std::function<boolean(Authentication & authentication)> AuthenticationPr
class AuthenticationPredicates { class AuthenticationPredicates {
public: public:
static bool NONE_REQUIRED(Authentication & authentication) { static bool NONE_REQUIRED(const Authentication & authentication) {
(void)authentication;
return true; return true;
}; };
static bool IS_AUTHENTICATED(Authentication & authentication) { static bool IS_AUTHENTICATED(const Authentication & authentication) {
return authentication.authenticated; return authentication.authenticated;
}; };
static bool IS_ADMIN(Authentication & authentication) { static bool IS_ADMIN(const Authentication & authentication) {
return authentication.authenticated && authentication.user->admin; return authentication.authenticated && authentication.user->admin;
}; };
}; };
@@ -76,7 +77,7 @@ class SecurityManager {
/* /*
* Generate a JWT for the user provided * Generate a JWT for the user provided
*/ */
virtual String generateJWT(User * user) = 0; virtual String generateJWT(const User * user) = 0;
/* /*
* Check the request header for the Authorization token * Check the request header for the Authorization token

View File

@@ -1,15 +1,13 @@
#include <SecuritySettingsService.h> #include "SecuritySettingsService.h"
#include "../../src/emsesp_stub.hpp"
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([&] { configureJWTHandler(); }, false); addUpdateHandler([this] { configureJWTHandler(); }, false);
server->on(GENERATE_TOKEN_PATH, server->on(GENERATE_TOKEN_PATH,
HTTP_GET, HTTP_GET,
wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN)); SecuritySettingsService::wrapRequest([this](AsyncWebServerRequest * request) { generateToken(request); }, AuthenticationPredicates::IS_ADMIN));
} }
void SecuritySettingsService::begin() { void SecuritySettingsService::begin() {
@@ -30,7 +28,7 @@ Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerReques
String value = tokenParamater->value(); String value = tokenParamater->value();
return authenticateJWT(value); return authenticateJWT(value);
} }
return Authentication(); return {};
} }
void SecuritySettingsService::configureJWTHandler() { void SecuritySettingsService::configureJWTHandler() {
@@ -43,37 +41,37 @@ Authentication SecuritySettingsService::authenticateJWT(String & jwt) {
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) { for (const User & _user : _state.users) {
if (_user.username == username && validatePayload(parsedPayload, &_user)) { if (_user.username == username && validatePayload(parsedPayload, &_user)) {
return Authentication(_user); return Authentication(_user);
} }
} }
} }
return Authentication(); return {};
} }
Authentication SecuritySettingsService::authenticate(const String & username, const String & password) { Authentication SecuritySettingsService::authenticate(const String & username, const String & password) {
for (User _user : _state.users) { for (const User & _user : _state.users) {
if (_user.username == username && _user.password == password) { if (_user.username == username && _user.password == password) {
return Authentication(_user); return Authentication(_user);
} }
} }
return Authentication(); return {};
} }
inline void populateJWTPayload(JsonObject payload, User * user) { inline void populateJWTPayload(JsonObject payload, const User * user) {
payload["username"] = user->username; payload["username"] = user->username;
payload["admin"] = user->admin; payload["admin"] = user->admin;
} }
boolean SecuritySettingsService::validatePayload(JsonObject parsedPayload, User * user) { boolean SecuritySettingsService::validatePayload(JsonObject parsedPayload, const User * user) {
JsonDocument jsonDocument; JsonDocument jsonDocument;
JsonObject payload = jsonDocument.to<JsonObject>(); JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user); populateJWTPayload(payload, user);
return payload == parsedPayload; return payload == parsedPayload;
} }
String SecuritySettingsService::generateJWT(User * user) { String SecuritySettingsService::generateJWT(const User * user) {
JsonDocument jsonDocument; JsonDocument jsonDocument;
JsonObject payload = jsonDocument.to<JsonObject>(); JsonObject payload = jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user); populateJWTPayload(payload, user);
@@ -111,11 +109,11 @@ ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequest
void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) { void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
AsyncWebParameter * usernameParam = request->getParam("username"); AsyncWebParameter * usernameParam = request->getParam("username");
for (User _user : _state.users) { for (const User & _user : _state.users) {
if (_user.username == usernameParam->value()) { if (_user.username == usernameParam->value()) {
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["token"] = generateJWT(&_user); root["token"] = generateJWT(&_user);
response->setLength(); response->setLength();
request->send(response); request->send(response);
return; return;
@@ -123,34 +121,3 @@ void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
} }
request->send(401); request->send(401);
} }
void SecuritySettings::read(SecuritySettings & settings, JsonObject root) {
// secret
root["jwt_secret"] = settings.jwtSecret;
// users
JsonArray users = root["users"].to<JsonArray>();
for (User user : settings.users) {
JsonObject userRoot = users.add<JsonObject>();
userRoot["username"] = user.username;
userRoot["password"] = user.password;
userRoot["admin"] = user.admin;
}
}
StateUpdateResult SecuritySettings::update(JsonObject root, SecuritySettings & settings) {
// secret
settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET;
// users
settings.users.clear();
if (root["users"].is<JsonArray>()) {
for (JsonVariant user : root["users"].as<JsonArray>()) {
settings.users.push_back(User(user["username"], user["password"], user["admin"]));
}
} else {
settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true));
settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false));
}
return StateUpdateResult::CHANGED;
}

View File

@@ -1,10 +1,10 @@
#ifndef SecuritySettingsService_h #ifndef SecuritySettingsService_h
#define SecuritySettingsService_h #define SecuritySettingsService_h
#include <Features.h> #include "Features.h"
#include <SecurityManager.h> #include "SecurityManager.h"
#include <HttpEndpoint.h> #include "HttpEndpoint.h"
#include <FSPersistence.h> #include "FSPersistence.h"
#ifndef FACTORY_ADMIN_USERNAME #ifndef FACTORY_ADMIN_USERNAME
#define FACTORY_ADMIN_USERNAME "admin" #define FACTORY_ADMIN_USERNAME "admin"
@@ -33,23 +33,51 @@ class SecuritySettings {
String jwtSecret; String jwtSecret;
std::vector<User> users; std::vector<User> users;
static void read(SecuritySettings & settings, JsonObject root); static void read(SecuritySettings & settings, JsonObject root) {
static StateUpdateResult update(JsonObject root, SecuritySettings & settings); // secret
root["jwt_secret"] = settings.jwtSecret;
// users
JsonArray users = root["users"].to<JsonArray>();
for (const User & user : settings.users) {
JsonObject userRoot = users.add<JsonObject>();
userRoot["username"] = user.username;
userRoot["password"] = user.password;
userRoot["admin"] = user.admin;
}
}
static StateUpdateResult update(JsonObject root, SecuritySettings & settings) {
// secret
settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET;
// users
settings.users.clear();
if (root["users"].is<JsonArray>()) {
for (JsonVariant user : root["users"].as<JsonArray>()) {
settings.users.emplace_back(user["username"], user["password"], user["admin"]);
}
} else {
settings.users.emplace_back(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
settings.users.emplace_back(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false);
}
return StateUpdateResult::CHANGED;
}
}; };
class SecuritySettingsService : public StatefulService<SecuritySettings>, public SecurityManager { class SecuritySettingsService final : 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) override;
Authentication authenticateRequest(AsyncWebServerRequest * request); Authentication authenticateRequest(AsyncWebServerRequest * request) override;
String generateJWT(User * user); String generateJWT(const User * user) override;
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) override;
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) override;
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate); ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate) override;
private: private:
HttpEndpoint<SecuritySettings> _httpEndpoint; HttpEndpoint<SecuritySettings> _httpEndpoint;
@@ -68,7 +96,7 @@ class SecuritySettingsService : public StatefulService<SecuritySettings>, public
/* /*
* Verify the payload is correct * Verify the payload is correct
*/ */
boolean validatePayload(JsonObject parsedPayload, User * user); boolean validatePayload(JsonObject parsedPayload, const User * user);
}; };
#endif #endif

View File

@@ -1,3 +1,3 @@
#include <StatefulService.h> #include "StatefulService.h"
update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0; update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0;

View File

@@ -1,21 +1,20 @@
#include <SystemStatus.h> #include "SystemStatus.h"
#include <esp_ota_ops.h> #include <esp_ota_ops.h>
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityManager) { SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(SYSTEM_STATUS_SERVICE_PATH, server->on(SYSTEM_STATUS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { systemStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
} }
void SystemStatus::systemStatus(AsyncWebServerRequest * request) { void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
emsesp::EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap emsesp::EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (DEBUG)"; root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (DEBUG)";
@@ -49,11 +48,11 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
root["psram_size"] = emsesp::EMSESP::system_.PSram(); root["psram_size"] = emsesp::EMSESP::system_.PSram();
root["free_psram"] = ESP.getFreePsram() / 1024; root["free_psram"] = ESP.getFreePsram() / 1024;
} }
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr);
if (partition != NULL) { // factory partition found if (partition != NULL) { // factory partition found
root["has_loader"] = true; root["has_loader"] = true;
} else { // check for not empty, smaller OTA partition } else { // check for not empty, smaller OTA partition
partition = esp_ota_get_next_update_partition(NULL); partition = esp_ota_get_next_update_partition(nullptr);
if (partition) { if (partition) {
uint64_t buffer; uint64_t buffer;
esp_partition_read(partition, 0, &buffer, 8); esp_partition_read(partition, 0, &buffer, 8);

View File

@@ -5,10 +5,10 @@
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <FS.h> #include <FS.h>
#include <LittleFS.h> #include <LittleFS.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include "SecurityManager.h"
#define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" #define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus"

View File

@@ -1,20 +1,27 @@
#include <UploadFileService.h> #include "UploadFileService.h"
#include <esp_ota_ops.h>
#include <esp_app_format.h>
#include "../../src/emsesp_stub.hpp" #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc #include <esp_app_format.h>
static bool is_firmware = false; static String getFilenameExtension(const String & filename) {
static char md5[33] = "\0"; const auto pos = filename.lastIndexOf('.');
if (pos != -1) {
return filename.substring(static_cast<unsigned int>(pos) + 1);
}
return {};
}
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager) UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
: _securityManager(securityManager) { : _securityManager(securityManager)
server->on(UPLOAD_FILE_PATH, , _is_firmware(false)
HTTP_POST, , _md5() {
std::bind(&UploadFileService::uploadComplete, this, _1), server->on(
std::bind(&UploadFileService::handleUpload, this, _1, _2, _3, _4, _5, _6)); UPLOAD_FILE_PATH,
HTTP_POST,
[this](AsyncWebServerRequest * request) { uploadComplete(request); },
[this](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
handleUpload(request, filename, index, data, len, final);
});
} }
void UploadFileService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) { void UploadFileService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
@@ -28,29 +35,27 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
// at init // at init
if (!index) { if (!index) {
// check details of the file, to see if its a valid bin or json file // check details of the file, to see if its a valid bin or json file
std::string fname(filename.c_str()); const String extension = getFilenameExtension(filename);
auto position = fname.find_last_of("."); const std::size_t filesize = request->contentLength();
std::string extension = fname.substr(position + 1);
size_t fsize = request->contentLength();
is_firmware = false; _is_firmware = false;
if ((extension == "bin") && (fsize > 1000000)) { if ((extension == "bin") && (filesize > 1000000)) {
is_firmware = true; _is_firmware = true;
} else if (extension == "json") { } else if (extension == "json") {
md5[0] = '\0'; // clear md5 _md5[0] = '\0'; // clear md5
} else if (extension == "md5") { } else if (extension == "md5") {
if (len == 32) { if (len == _md5.size() - 1) {
memcpy(md5, data, 32); std::memcpy(_md5.data(), data, _md5.size() - 1);
md5[32] = '\0'; _md5.back() = '\0';
} }
return; return;
} else { } else {
md5[0] = '\0'; _md5.front() = '\0';
handleError(request, 406); // Not Acceptable - unsupported file type handleError(request, 406); // Not Acceptable - unsupported file type
return; return;
} }
if (is_firmware) { if (_is_firmware) {
// Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5 // Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 #if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) { if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) {
@@ -74,12 +79,12 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
} }
#endif #endif
// it's firmware - initialize the ArduinoOTA updater // it's firmware - initialize the ArduinoOTA updater
if (Update.begin(fsize - sizeof(esp_image_header_t))) { if (Update.begin(filesize - sizeof(esp_image_header_t))) {
if (strlen(md5) == 32) { if (strlen(_md5.data()) == _md5.size() - 1) {
Update.setMD5(md5); Update.setMD5(_md5.data());
md5[0] = '\0'; _md5.front() = '\0';
} }
request->onDisconnect(UploadFileService::handleEarlyDisconnect); // success, let's make sure we end the update if the client hangs up request->onDisconnect([this]() { handleEarlyDisconnect(); }); // success, let's make sure we end the update if the client hangs up
} else { } else {
handleError(request, 507); // failed to begin, send an error response Insufficient Storage handleError(request, 507); // failed to begin, send an error response Insufficient Storage
return; return;
@@ -90,23 +95,17 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
} }
} }
if (!is_firmware) { if (!_is_firmware) {
if (len) { if (len && len != request->_tempFile.write(data, len)) { // stream the incoming chunk to the opened file
if (len != request->_tempFile.write(data, len)) { // stream the incoming chunk to the opened file handleError(request, 507); // 507-Insufficient Storage
handleError(request, 507); // 507-Insufficient Storage
}
} }
} else { } else if (!request->_tempObject) { // if we haven't delt with an error, continue with the firmware update
// if we haven't delt with an error, continue with the firmware update if (Update.write(data, len) != len) {
if (!request->_tempObject) { handleError(request, 500);
if (Update.write(data, len) != len) { return;
handleError(request, 500); }
} if (final && !Update.end(true)) {
if (final) { handleError(request, 500);
if (!Update.end(true)) {
handleError(request, 500);
}
}
} }
} }
} }
@@ -124,7 +123,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
// check if it was a firmware upgrade // check if it was a firmware upgrade
// if no error, send the success response as a JSON // if no error, send the success response as a JSON
if (is_firmware && !request->_tempObject) { if (_is_firmware && !request->_tempObject) {
emsesp::EMSESP::system_.store_nvs_values(); emsesp::EMSESP::system_.store_nvs_values();
request->onDisconnect(RestartService::restartNow); request->onDisconnect(RestartService::restartNow);
AsyncWebServerResponse * response = request->beginResponse(200); AsyncWebServerResponse * response = request->beginResponse(200);
@@ -132,10 +131,10 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
return; return;
} }
if (strlen(md5) == 32) { if (strlen(_md5.data()) == _md5.size() - 1) {
auto * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["md5"] = md5; root["md5"] = _md5.data();
response->setLength(); response->setLength();
request->send(response); request->send(response);
return; return;
@@ -163,6 +162,6 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
} }
void UploadFileService::handleEarlyDisconnect() { void UploadFileService::handleEarlyDisconnect() {
is_firmware = false; _is_firmware = false;
Update.abort(); Update.abort();
} }

View File

@@ -1,16 +1,16 @@
#ifndef UploadFileService_h #ifndef UploadFileService_h
#define UploadFileService_h #define UploadFileService_h
#include <Arduino.h> #include "RestartService.h"
#include "SecurityManager.h"
#include <Arduino.h>
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>
#include <Update.h> #include <Update.h>
#include <WiFi.h> #include <WiFi.h>
#include <LittleFS.h> #include <array>
#include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include <RestartService.h>
#define UPLOAD_FILE_PATH "/rest/uploadFile" #define UPLOAD_FILE_PATH "/rest/uploadFile"
#define TEMP_FILENAME_PATH "/tmp_upload" #define TEMP_FILENAME_PATH "/tmp_upload"
@@ -20,11 +20,14 @@ class UploadFileService {
UploadFileService(AsyncWebServer * server, SecurityManager * securityManager); UploadFileService(AsyncWebServer * server, SecurityManager * securityManager);
private: private:
SecurityManager * _securityManager; SecurityManager * _securityManager;
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final); bool _is_firmware;
void uploadComplete(AsyncWebServerRequest * request); std::array<char, 33> _md5;
void handleError(AsyncWebServerRequest * request, int code);
static void handleEarlyDisconnect(); void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
void uploadComplete(AsyncWebServerRequest * request);
void handleError(AsyncWebServerRequest * request, int code);
void handleEarlyDisconnect();
}; };
#endif #endif

View File

@@ -1,14 +1,12 @@
#include <WiFiScanner.h> #include "WiFiScanner.h"
using namespace std::placeholders; // for `_1` etc
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, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { scanNetworks(request); }, 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, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { listNetworks(request); }, AuthenticationPredicates::IS_ADMIN));
}; };
void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) { void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
@@ -21,18 +19,18 @@ void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
} }
void WiFiScanner::listNetworks(AsyncWebServerRequest * request) { void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
int numNetworks = WiFi.scanComplete(); const int numNetworks = WiFi.scanComplete();
if (numNetworks > -1) { if (numNetworks > -1) {
AsyncJsonResponse * response = new AsyncJsonResponse(false); auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
JsonArray networks = root["networks"].to<JsonArray>(); JsonArray networks = root["networks"].to<JsonArray>();
for (int i = 0; i < numNetworks; i++) { for (uint8_t i = 0; i < numNetworks; i++) {
JsonObject network = networks.add<JsonObject>(); JsonObject network = networks.add<JsonObject>();
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);
network["encryption_type"] = (uint8_t)WiFi.encryptionType(i); network["encryption_type"] = static_cast<uint8_t>(WiFi.encryptionType(i));
} }
response->setLength(); response->setLength();
request->send(response); request->send(response);

View File

@@ -3,10 +3,10 @@
#include <WiFi.h> #include <WiFi.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include "SecurityManager.h"
#define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks" #define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks"
#define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks" #define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks"

View File

@@ -68,7 +68,7 @@ class StatefulService {
} }
} }
StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater, ) { StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(_state); StateUpdateResult result = stateUpdater(_state);
endTransaction(); endTransaction();
@@ -85,7 +85,7 @@ class StatefulService {
return result; return result;
} }
StateUpdateResult update(JsonObject jsonObject, JsonStateUpdater<T> stateUpdater, ) { StateUpdateResult update(JsonObject jsonObject, JsonStateUpdater<T> stateUpdater) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state); StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction(); endTransaction();

View File

@@ -38,8 +38,7 @@
#endif #endif
#include <Preferences.h> #include <Preferences.h>
#include <ESP8266React.h> #include "ESP8266React.h"
#include "web/WebStatusService.h" #include "web/WebStatusService.h"
#include "web/WebDataService.h" #include "web/WebDataService.h"
#include "web/WebSettingsService.h" #include "web/WebSettingsService.h"

View File

@@ -24,8 +24,7 @@
#include "version.h" #include "version.h"
#include "default_settings.h" #include "default_settings.h"
#include "helpers.h" #include "helpers.h"
#include "ESP8266React.h"
#include <ESP8266React.h>
#include <uuid/log.h> #include <uuid/log.h>

View File

@@ -21,16 +21,15 @@
#include <string> #include <string>
#include <deque> #include <deque>
#include <uuid/log.h>
// UART drivers // UART drivers
#if defined(ESP32) #if defined(ESP32)
#include "uart/emsuart_esp32.h" #include "uart/emsuart_esp32.h"
#elif defined(EMSESP_STANDALONE) #elif defined(EMSESP_STANDALONE)
#include <emsuart_standalone.h> #include "emsuart_standalone.h"
#endif #endif
#include <uuid/log.h>
#include "helpers.h" #include "helpers.h"
#define MAX_RX_TELEGRAMS 10 // size of Rx queue #define MAX_RX_TELEGRAMS 10 // size of Rx queue

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.5-dev.13" #define EMSESP_APP_VERSION "3.6.5-dev.14"

View File

@@ -18,8 +18,6 @@
#include "emsesp.h" #include "emsesp.h"
using namespace std::placeholders; // for `_1` etc
namespace emsesp { namespace emsesp {
uint32_t WebAPIService::api_count_ = 0; uint32_t WebAPIService::api_count_ = 0;
@@ -27,17 +25,26 @@ uint16_t WebAPIService::api_fails_ = 0;
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager) WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
: _securityManager(securityManager) : _securityManager(securityManager)
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2)) { // for POSTS, must use 'Content-Type: application/json' in header , _apiHandler(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService_post(request, json); }) { // for POSTs
server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS server->on(EMSESP_API_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { webAPIService_get(request); }); // for GETs
server->addHandler(&_apiHandler); server->addHandler(&_apiHandler);
// for settings // for settings
server->on(GET_SETTINGS_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getSettings, this, _1), AuthenticationPredicates::IS_ADMIN)); server->on(GET_SETTINGS_PATH,
HTTP_GET,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getSettings(request); }, AuthenticationPredicates::IS_ADMIN));
server->on(GET_CUSTOMIZATIONS_PATH, server->on(GET_CUSTOMIZATIONS_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebAPIService::getCustomizations, this, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getCustomizations(request); }, AuthenticationPredicates::IS_ADMIN));
server->on(GET_SCHEDULE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getSchedule, this, _1), AuthenticationPredicates::IS_ADMIN));
server->on(GET_ENTITIES_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getEntities, this, _1), AuthenticationPredicates::IS_ADMIN)); server->on(GET_SCHEDULE_PATH,
HTTP_GET,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getSchedule(request); }, AuthenticationPredicates::IS_ADMIN));
server->on(GET_ENTITIES_PATH,
HTTP_GET,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getEntities(request); }, AuthenticationPredicates::IS_ADMIN));
} }
// HTTP GET // HTTP GET

View File

@@ -20,8 +20,6 @@
namespace emsesp { namespace emsesp {
using namespace std::placeholders; // for `_1` etc
WebCustomEntityService::WebCustomEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) WebCustomEntityService::WebCustomEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebCustomEntity::read, : _httpEndpoint(WebCustomEntity::read,
WebCustomEntity::update, WebCustomEntity::update,

View File

@@ -20,27 +20,24 @@
namespace emsesp { namespace emsesp {
using namespace std::placeholders; // for `_1` etc
bool WebCustomization::_start = true; bool WebCustomization::_start = true;
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE) : _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
, _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH, , _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH,
securityManager->wrapCallback(std::bind(&WebCustomizationService::customization_entities, this, _1, _2), securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { customization_entities(request, json); },
AuthenticationPredicates::IS_AUTHENTICATED)) { AuthenticationPredicates::IS_AUTHENTICATED)) {
server->on(DEVICE_ENTITIES_PATH, server->on(DEVICE_ENTITIES_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebCustomizationService::device_entities, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { device_entities(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
server->on(DEVICES_SERVICE_PATH, server->on(DEVICES_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebCustomizationService::devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { devices(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
server->on(RESET_CUSTOMIZATION_SERVICE_PATH, server->on(RESET_CUSTOMIZATION_SERVICE_PATH,
HTTP_POST, HTTP_POST,
securityManager->wrapRequest(std::bind(&WebCustomizationService::reset_customization, this, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { reset_customization(request); }, AuthenticationPredicates::IS_ADMIN));
_masked_entities_handler.setMethod(HTTP_POST); _masked_entities_handler.setMethod(HTTP_POST);
_masked_entities_handler.setMaxContentLength(2048); _masked_entities_handler.setMaxContentLength(2048);

View File

@@ -20,33 +20,36 @@
namespace emsesp { namespace emsesp {
using namespace std::placeholders; // for `_1` etc
WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager) WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager)
: _write_value_handler(WRITE_DEVICE_VALUE_SERVICE_PATH, : _write_value_handler(WRITE_DEVICE_VALUE_SERVICE_PATH,
securityManager->wrapCallback(std::bind(&WebDataService::write_device_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_device_value(request, json); },
AuthenticationPredicates::IS_ADMIN))
, _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH, , _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH,
securityManager->wrapCallback(std::bind(&WebDataService::write_temperature_sensor, this, _1, _2), securityManager->wrapCallback([this](AsyncWebServerRequest * request,
JsonVariant json) { write_temperature_sensor(request, json); },
AuthenticationPredicates::IS_ADMIN)) AuthenticationPredicates::IS_ADMIN))
, _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH, , _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH,
securityManager->wrapCallback(std::bind(&WebDataService::write_analog_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_analog_sensor(request, json); },
AuthenticationPredicates::IS_ADMIN)) {
// GET's // GET's
server->on(DEVICE_DATA_SERVICE_PATH, server->on(DEVICE_DATA_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebDataService::device_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { device_data(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
server->on(CORE_DATA_SERVICE_PATH, server->on(CORE_DATA_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebDataService::core_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { core_data(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
server->on(SENSOR_DATA_SERVICE_PATH, server->on(SENSOR_DATA_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebDataService::sensor_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { sensor_data(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
// POST's // POST's
server->on(SCAN_DEVICES_SERVICE_PATH, server->on(SCAN_DEVICES_SERVICE_PATH,
HTTP_POST, HTTP_POST,
securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { scan_devices(request); }, AuthenticationPredicates::IS_ADMIN));
_write_value_handler.setMethod(HTTP_POST); _write_value_handler.setMethod(HTTP_POST);
_write_value_handler.setMaxContentLength(256); _write_value_handler.setMaxContentLength(256);

View File

@@ -18,19 +18,18 @@
#include "emsesp.h" #include "emsesp.h"
using namespace std::placeholders;
namespace emsesp { namespace emsesp {
WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager) WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager)
: events_(EVENT_SOURCE_LOG_PATH) : events_(EVENT_SOURCE_LOG_PATH)
, setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2)) { , setValues_(LOG_SETTINGS_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { setValues(request, json); }) {
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
server->on(LOG_SETTINGS_PATH, HTTP_GET, std::bind(&WebLogService::getValues, this, _1)); // get settings // get settings
server->on(LOG_SETTINGS_PATH, HTTP_POST, [this](AsyncWebServerRequest * request) { getValues(request); });
// for bring back the whole log - is a command, hence a POST // for bring back the whole log - is a command, hence a POST
server->on(FETCH_LOG_PATH, HTTP_POST, std::bind(&WebLogService::fetchLog, this, _1)); server->on(FETCH_LOG_PATH, HTTP_POST, [this](AsyncWebServerRequest * request) { fetchLog(request); });
server->addHandler(&setValues_); server->addHandler(&setValues_);
server->addHandler(&events_); server->addHandler(&events_);

View File

@@ -21,8 +21,6 @@
namespace emsesp { namespace emsesp {
using namespace std::placeholders; // for `_1` etc
WebSchedulerService::WebSchedulerService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) WebSchedulerService::WebSchedulerService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebScheduler::read, WebScheduler::update, this, server, EMSESP_SCHEDULER_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED) : _httpEndpoint(WebScheduler::read, WebScheduler::update, this, server, EMSESP_SCHEDULER_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED)
, _fsPersistence(WebScheduler::read, WebScheduler::update, this, fs, EMSESP_SCHEDULER_FILE) { , _fsPersistence(WebScheduler::read, WebScheduler::update, this, fs, EMSESP_SCHEDULER_FILE) {

View File

@@ -22,17 +22,13 @@ namespace emsesp {
uint8_t WebSettings::flags_ = 0; uint8_t WebSettings::flags_ = 0;
using namespace std::placeholders; // for `_1` etc
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager) : _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
, _fsPersistence(WebSettings::read, WebSettings::update, this, fs, EMSESP_SETTINGS_FILE) { , _fsPersistence(WebSettings::read, WebSettings::update, this, fs, EMSESP_SETTINGS_FILE) {
// GET
server->on(EMSESP_BOARD_PROFILE_SERVICE_PATH, server->on(EMSESP_BOARD_PROFILE_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebSettingsService::board_profile, this, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { board_profile(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
addUpdateHandler([this] { onUpdate(); }, false);
addUpdateHandler([&] { onUpdate(); }, false);
} }
void WebSettings::read(WebSettings & settings, JsonObject root) { void WebSettings::read(WebSettings & settings, JsonObject root) {

View File

@@ -18,14 +18,12 @@
#include "emsesp.h" #include "emsesp.h"
using namespace std::placeholders; // for `_1` etc
namespace emsesp { namespace emsesp {
WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * securityManager) { WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * securityManager) {
server->on(EMSESP_STATUS_SERVICE_PATH, server->on(EMSESP_STATUS_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebStatusService::webStatusService, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { webStatusService(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
} }
void WebStatusService::webStatusService(AsyncWebServerRequest * request) { void WebStatusService::webStatusService(AsyncWebServerRequest * request) {