Upgrades ArduinoJson from version 6 to 7
This commit is contained in:
@@ -33,7 +33,7 @@ void APStatus::begin()
|
|||||||
|
|
||||||
esp_err_t APStatus::apStatus(PsychicRequest *request)
|
esp_err_t APStatus::apStatus(PsychicRequest *request)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_AP_STATUS_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
|
|
||||||
root["status"] = _apSettingsService->getAPNetworkStatus();
|
root["status"] = _apSettingsService->getAPNetworkStatus();
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
#include <APSettingsService.h>
|
#include <APSettingsService.h>
|
||||||
|
|
||||||
#define MAX_AP_STATUS_SIZE 1024
|
|
||||||
#define AP_STATUS_SERVICE_PATH "/api/apStatus"
|
#define AP_STATUS_SERVICE_PATH "/api/apStatus"
|
||||||
|
|
||||||
class APStatus
|
class APStatus
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class AnalyticsService
|
|||||||
updateAnalytics();
|
updateAnalytics();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
StaticJsonDocument<MAX_ESP_ANALYTICS_SIZE> doc;
|
JsonDocument doc;
|
||||||
char message[MAX_ESP_ANALYTICS_SIZE];
|
char message[MAX_ESP_ANALYTICS_SIZE];
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -65,10 +65,10 @@ class AnalyticsService
|
|||||||
doc["cpu1_usage"] = _taskManager->getCpuUsage(1);
|
doc["cpu1_usage"] = _taskManager->getCpuUsage(1);
|
||||||
doc["cpu_usage"] = _taskManager->getCpuUsage();
|
doc["cpu_usage"] = _taskManager->getCpuUsage();
|
||||||
// Add _taskManager->getTaskNames() as a JSON array
|
// Add _taskManager->getTaskNames() as a JSON array
|
||||||
JsonArray tasks = doc.createNestedArray("tasks");
|
JsonArray tasks = doc["tasks"].as<JsonArray>();
|
||||||
for (auto const &task : _taskManager->getTasks())
|
for (auto const &task : _taskManager->getTasks())
|
||||||
{
|
{
|
||||||
JsonObject nested = tasks.createNestedObject();
|
JsonObject nested = tasks.add<JsonObject>();
|
||||||
nested["name"] = task.name;
|
nested["name"] = task.name;
|
||||||
nested["stackSize"] = task.stackSize;
|
nested["stackSize"] = task.stackSize;
|
||||||
nested["priority"] = task.priority;
|
nested["priority"] = task.priority;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ void AuthenticationService::begin()
|
|||||||
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) {
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, 256);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
root["access_token"] = _securityManager->generateJWT(authentication.user);
|
root["access_token"] = _securityManager->generateJWT(authentication.user);
|
||||||
return response.send();
|
return response.send();
|
||||||
|
|||||||
@@ -22,8 +22,6 @@
|
|||||||
#define VERIFY_AUTHORIZATION_PATH "/api/verifyAuthorization"
|
#define VERIFY_AUTHORIZATION_PATH "/api/verifyAuthorization"
|
||||||
#define SIGN_IN_PATH "/api/signIn"
|
#define SIGN_IN_PATH "/api/signIn"
|
||||||
|
|
||||||
#define MAX_AUTHENTICATION_SIZE 256
|
|
||||||
|
|
||||||
#if FT_ENABLED(FT_SECURITY)
|
#if FT_ENABLED(FT_SECURITY)
|
||||||
|
|
||||||
class AuthenticationService
|
class AuthenticationService
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ void BatteryService::begin()
|
|||||||
|
|
||||||
void BatteryService::batteryEvent()
|
void BatteryService::batteryEvent()
|
||||||
{
|
{
|
||||||
StaticJsonDocument<32> doc;
|
JsonDocument doc;
|
||||||
char message[32];
|
char message[32];
|
||||||
doc["soc"] = _lastSOC;
|
doc["soc"] = _lastSOC;
|
||||||
doc["charging"] = _isCharging;
|
doc["charging"] = _isCharging;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ extern const uint8_t rootca_crt_bundle_start[] asm("_binary_src_certs_x509_crt_b
|
|||||||
|
|
||||||
static EventSocket *_socket = nullptr;
|
static EventSocket *_socket = nullptr;
|
||||||
static int previousProgress = 0;
|
static int previousProgress = 0;
|
||||||
StaticJsonDocument<128> doc;
|
JsonDocument doc;
|
||||||
|
|
||||||
void update_started()
|
void update_started()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server, unsigned int numberEnd
|
|||||||
|
|
||||||
void ESP32SvelteKit::begin() {
|
void ESP32SvelteKit::begin() {
|
||||||
ESP_LOGV("ESP32SvelteKit", "Loading settings from files system");
|
ESP_LOGV("ESP32SvelteKit", "Loading settings from files system");
|
||||||
ESP_LOGI("Running Firmware Version: %s\n", APP_VERSION);
|
ESP_LOGI("Running Firmware Version: %s", APP_VERSION);
|
||||||
ESPFS.begin(true);
|
ESPFS.begin(true);
|
||||||
|
|
||||||
_wifiSettingsService.initWiFi();
|
_wifiSettingsService.initWiFi();
|
||||||
|
|||||||
@@ -27,12 +27,10 @@ public:
|
|||||||
EventEndpoint(JsonStateReader<T> stateReader,
|
EventEndpoint(JsonStateReader<T> stateReader,
|
||||||
JsonStateUpdater<T> stateUpdater,
|
JsonStateUpdater<T> stateUpdater,
|
||||||
StatefulService<T> *statefulService,
|
StatefulService<T> *statefulService,
|
||||||
EventSocket *socket, const char *event,
|
EventSocket *socket, const char *event) : _stateReader(stateReader),
|
||||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
|
||||||
_stateUpdater(stateUpdater),
|
_stateUpdater(stateUpdater),
|
||||||
_statefulService(statefulService),
|
_statefulService(statefulService),
|
||||||
_socket(socket),
|
_socket(socket),
|
||||||
_bufferSize(bufferSize),
|
|
||||||
_event(event)
|
_event(event)
|
||||||
{
|
{
|
||||||
_statefulService->addUpdateHandler([&](const String &originId)
|
_statefulService->addUpdateHandler([&](const String &originId)
|
||||||
@@ -53,7 +51,6 @@ private:
|
|||||||
StatefulService<T> *_statefulService;
|
StatefulService<T> *_statefulService;
|
||||||
EventSocket *_socket;
|
EventSocket *_socket;
|
||||||
const char *_event;
|
const char *_event;
|
||||||
size_t _bufferSize;
|
|
||||||
|
|
||||||
void updateState(JsonObject &root, int originId)
|
void updateState(JsonObject &root, int originId)
|
||||||
{
|
{
|
||||||
@@ -62,7 +59,7 @@ private:
|
|||||||
|
|
||||||
void syncState(const String &originId, bool sync = false)
|
void syncState(const String &originId, bool sync = false)
|
||||||
{
|
{
|
||||||
DynamicJsonDocument jsonDocument{_bufferSize};
|
JsonDocument jsonDocument;
|
||||||
JsonObject root = jsonDocument.to<JsonObject>();
|
JsonObject root = jsonDocument.to<JsonObject>();
|
||||||
String output;
|
String output;
|
||||||
_statefulService->read(root, _stateReader);
|
_statefulService->read(root, _stateReader);
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ EventSocket::EventSocket(PsychicHttpServer *server,
|
|||||||
SecurityManager *securityManager,
|
SecurityManager *securityManager,
|
||||||
AuthenticationPredicate authenticationPredicate) : _server(server),
|
AuthenticationPredicate authenticationPredicate) : _server(server),
|
||||||
_securityManager(securityManager),
|
_securityManager(securityManager),
|
||||||
_authenticationPredicate(authenticationPredicate),
|
_authenticationPredicate(authenticationPredicate)
|
||||||
_bufferSize(1024)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,11 +18,6 @@ void EventSocket::begin()
|
|||||||
_socket.onFrame(std::bind(&EventSocket::onFrame, this, std::placeholders::_1, std::placeholders::_2));
|
_socket.onFrame(std::bind(&EventSocket::onFrame, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
_server->on(EVENT_SERVICE_PATH, &_socket);
|
_server->on(EVENT_SERVICE_PATH, &_socket);
|
||||||
|
|
||||||
registerEvent("errorToast");
|
|
||||||
registerEvent("warningToast");
|
|
||||||
registerEvent("infoToast");
|
|
||||||
registerEvent("successToast");
|
|
||||||
|
|
||||||
ESP_LOGV("EventSocket", "Registered event socket endpoint: %s", EVENT_SERVICE_PATH);
|
ESP_LOGV("EventSocket", "Registered event socket endpoint: %s", EVENT_SERVICE_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +58,7 @@ esp_err_t EventSocket::onFrame(PsychicWebSocketRequest *request, httpd_ws_frame
|
|||||||
ESP_LOGV("EventSocket", "ws[%s][%u] request: %s", request->client()->remoteIP().toString().c_str(),
|
ESP_LOGV("EventSocket", "ws[%s][%u] request: %s", request->client()->remoteIP().toString().c_str(),
|
||||||
request->client()->socket(), (char *)frame->payload);
|
request->client()->socket(), (char *)frame->payload);
|
||||||
|
|
||||||
DynamicJsonDocument doc = DynamicJsonDocument(_bufferSize);
|
JsonDocument doc;
|
||||||
DeserializationError error = deserializeJson(doc, (char *)frame->payload, frame->len);
|
DeserializationError error = deserializeJson(doc, (char *)frame->payload, frame->len);
|
||||||
|
|
||||||
if (!error && doc.is<JsonObject>())
|
if (!error && doc.is<JsonObject>())
|
||||||
@@ -159,30 +153,6 @@ void EventSocket::emit(const char *event, const char *payload, const char *origi
|
|||||||
xSemaphoreGive(clientSubscriptionsMutex);
|
xSemaphoreGive(clientSubscriptionsMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventSocket::pushNotification(String message, pushEvent event)
|
|
||||||
{
|
|
||||||
String eventType;
|
|
||||||
switch (event)
|
|
||||||
{
|
|
||||||
case (PUSHERROR):
|
|
||||||
eventType = "errorToast";
|
|
||||||
break;
|
|
||||||
case (PUSHWARNING):
|
|
||||||
eventType = "warningToast";
|
|
||||||
break;
|
|
||||||
case (PUSHINFO):
|
|
||||||
eventType = "infoToast";
|
|
||||||
break;
|
|
||||||
case (PUSHSUCCESS):
|
|
||||||
eventType = "successToast";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ESP_LOGW("EventSocket", "Client tried invalid push notification: %s", event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emit(eventType.c_str(), message.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventSocket::handleEventCallbacks(String event, JsonObject &jsonObject, int originId)
|
void EventSocket::handleEventCallbacks(String event, JsonObject &jsonObject, int originId)
|
||||||
{
|
{
|
||||||
for (auto &callback : event_callbacks[event])
|
for (auto &callback : event_callbacks[event])
|
||||||
|
|||||||
@@ -13,14 +13,6 @@
|
|||||||
typedef std::function<void(JsonObject &root, int originId)> EventCallback;
|
typedef std::function<void(JsonObject &root, int originId)> EventCallback;
|
||||||
typedef std::function<void(const String &originId, bool sync)> SubscribeCallback;
|
typedef std::function<void(const String &originId, bool sync)> SubscribeCallback;
|
||||||
|
|
||||||
enum pushEvent
|
|
||||||
{
|
|
||||||
PUSHERROR,
|
|
||||||
PUSHWARNING,
|
|
||||||
PUSHINFO,
|
|
||||||
PUSHSUCCESS
|
|
||||||
};
|
|
||||||
|
|
||||||
class EventSocket
|
class EventSocket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -41,8 +33,6 @@ public:
|
|||||||
void emit(const char *event, const char *payload, const char *originId, bool onlyToSameOrigin = false);
|
void emit(const char *event, const char *payload, const char *originId, bool onlyToSameOrigin = false);
|
||||||
// if onlyToSameOrigin == true, the message will be sent to the originId only, otherwise it will be broadcasted to all clients except the originId
|
// if onlyToSameOrigin == true, the message will be sent to the originId only, otherwise it will be broadcasted to all clients except the originId
|
||||||
|
|
||||||
void pushNotification(String message, pushEvent event);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PsychicHttpServer *_server;
|
PsychicHttpServer *_server;
|
||||||
PsychicWebSocketHandler _socket;
|
PsychicWebSocketHandler _socket;
|
||||||
@@ -58,7 +48,6 @@ private:
|
|||||||
|
|
||||||
bool isEventValid(String event);
|
bool isEventValid(String event);
|
||||||
|
|
||||||
size_t _bufferSize;
|
|
||||||
void onWSOpen(PsychicWebSocketClient *client);
|
void onWSOpen(PsychicWebSocketClient *client);
|
||||||
void onWSClose(PsychicWebSocketClient *client);
|
void onWSClose(PsychicWebSocketClient *client);
|
||||||
esp_err_t onFrame(PsychicWebSocketRequest *request, httpd_ws_frame *frame);
|
esp_err_t onFrame(PsychicWebSocketRequest *request, httpd_ws_frame *frame);
|
||||||
|
|||||||
@@ -15,58 +15,55 @@
|
|||||||
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#include <StatefulService.h>
|
|
||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
|
#include <StatefulService.h>
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class FSPersistence
|
class FSPersistence {
|
||||||
{
|
public:
|
||||||
public:
|
|
||||||
FSPersistence(JsonStateReader<T> stateReader,
|
FSPersistence(JsonStateReader<T> stateReader,
|
||||||
JsonStateUpdater<T> stateUpdater,
|
JsonStateUpdater<T> stateUpdater,
|
||||||
StatefulService<T> *statefulService,
|
StatefulService<T> *statefulService, FS *fs,
|
||||||
FS *fs,
|
const char *filePath)
|
||||||
const char *filePath,
|
: _stateReader(stateReader),
|
||||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
|
||||||
_stateUpdater(stateUpdater),
|
_stateUpdater(stateUpdater),
|
||||||
_statefulService(statefulService),
|
_statefulService(statefulService),
|
||||||
_fs(fs),
|
_fs(fs),
|
||||||
_filePath(filePath),
|
_filePath(filePath),
|
||||||
_bufferSize(bufferSize),
|
_updateHandlerId(0) {
|
||||||
_updateHandlerId(0)
|
|
||||||
{
|
|
||||||
enableUpdateHandler();
|
enableUpdateHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
void readFromFS()
|
void readFromFS() {
|
||||||
{
|
|
||||||
File settingsFile = _fs->open(_filePath, "r");
|
File settingsFile = _fs->open(_filePath, "r");
|
||||||
|
|
||||||
if (settingsFile)
|
if (settingsFile) {
|
||||||
{
|
JsonDocument jsonDocument;
|
||||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
DeserializationError error =
|
||||||
DeserializationError error = deserializeJson(jsonDocument, settingsFile);
|
deserializeJson(jsonDocument, settingsFile);
|
||||||
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>())
|
if (error == DeserializationError::Ok &&
|
||||||
{
|
jsonDocument.is<JsonObject>()) {
|
||||||
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
||||||
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
_statefulService->updateWithoutPropagation(jsonObject,
|
||||||
|
_stateUpdater);
|
||||||
settingsFile.close();
|
settingsFile.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
settingsFile.close();
|
settingsFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we reach here we have not been successful in loading the config and hard-coded defaults are now applied.
|
// If we reach here we have not been successful in loading the config
|
||||||
// The settings are then written back to the file system so the defaults persist between resets. This last step is
|
// and hard-coded defaults are now applied. The settings are then
|
||||||
// required as in some cases defaults contain randomly generated values which would otherwise be modified on reset.
|
// written back to the file system so the defaults persist between
|
||||||
|
// resets. This last step is required as in some cases defaults contain
|
||||||
|
// randomly generated values which would otherwise be modified on reset.
|
||||||
applyDefaults();
|
applyDefaults();
|
||||||
writeToFS();
|
writeToFS();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool writeToFS()
|
bool writeToFS() {
|
||||||
{
|
|
||||||
// create and populate a new json object
|
// create and populate a new json object
|
||||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
JsonDocument jsonDocument;
|
||||||
JsonObject jsonObject = jsonDocument.to<JsonObject>();
|
JsonObject jsonObject = jsonDocument.to<JsonObject>();
|
||||||
_statefulService->read(jsonObject, _stateReader);
|
_statefulService->read(jsonObject, _stateReader);
|
||||||
|
|
||||||
@@ -77,8 +74,7 @@ public:
|
|||||||
File settingsFile = _fs->open(_filePath, "w");
|
File settingsFile = _fs->open(_filePath, "w");
|
||||||
|
|
||||||
// failed to open file, return false
|
// failed to open file, return false
|
||||||
if (!settingsFile)
|
if (!settingsFile) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,25 +84,21 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disableUpdateHandler()
|
void disableUpdateHandler() {
|
||||||
{
|
if (_updateHandlerId) {
|
||||||
if (_updateHandlerId)
|
|
||||||
{
|
|
||||||
_statefulService->removeUpdateHandler(_updateHandlerId);
|
_statefulService->removeUpdateHandler(_updateHandlerId);
|
||||||
_updateHandlerId = 0;
|
_updateHandlerId = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void enableUpdateHandler()
|
void enableUpdateHandler() {
|
||||||
{
|
if (!_updateHandlerId) {
|
||||||
if (!_updateHandlerId)
|
_updateHandlerId = _statefulService->addUpdateHandler(
|
||||||
{
|
[&](const String &originId) { writeToFS(); });
|
||||||
_updateHandlerId = _statefulService->addUpdateHandler([&](const String &originId)
|
|
||||||
{ writeToFS(); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JsonStateReader<T> _stateReader;
|
JsonStateReader<T> _stateReader;
|
||||||
JsonStateUpdater<T> _stateUpdater;
|
JsonStateUpdater<T> _stateUpdater;
|
||||||
StatefulService<T> *_statefulService;
|
StatefulService<T> *_statefulService;
|
||||||
@@ -115,28 +107,25 @@ private:
|
|||||||
size_t _bufferSize;
|
size_t _bufferSize;
|
||||||
update_handler_id_t _updateHandlerId;
|
update_handler_id_t _updateHandlerId;
|
||||||
|
|
||||||
// We assume we have a _filePath with format "/directory1/directory2/filename"
|
// We assume we have a _filePath with format
|
||||||
// We create a directory for each missing parent
|
// "/directory1/directory2/filename" We create a directory for each missing
|
||||||
void mkdirs()
|
// parent
|
||||||
{
|
void mkdirs() {
|
||||||
String path(_filePath);
|
String path(_filePath);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while ((index = path.indexOf('/', index + 1)) != -1)
|
while ((index = path.indexOf('/', index + 1)) != -1) {
|
||||||
{
|
|
||||||
String segment = path.substring(0, index);
|
String segment = path.substring(0, index);
|
||||||
if (!_fs->exists(segment))
|
if (!_fs->exists(segment)) {
|
||||||
{
|
|
||||||
_fs->mkdir(segment);
|
_fs->mkdir(segment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// We assume the updater supplies sensible defaults if an empty object
|
// We assume the updater supplies sensible defaults if an empty object
|
||||||
// is supplied, this virtual function allows that to be changed.
|
// is supplied, this virtual function allows that to be changed.
|
||||||
virtual void applyDefaults()
|
virtual void applyDefaults() {
|
||||||
{
|
JsonDocument jsonDocument;
|
||||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
|
||||||
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
||||||
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
#include <FactoryResetService.h>
|
#include <FactoryResetService.h>
|
||||||
|
|
||||||
using namespace std::placeholders;
|
|
||||||
|
|
||||||
FactoryResetService::FactoryResetService(PsychicHttpServer *server,
|
FactoryResetService::FactoryResetService(PsychicHttpServer *server,
|
||||||
FS *fs,
|
FS *fs,
|
||||||
SecurityManager *securityManager) : _server(server),
|
SecurityManager *securityManager) : _server(server),
|
||||||
@@ -28,7 +26,7 @@ void FactoryResetService::begin()
|
|||||||
{
|
{
|
||||||
_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(std::bind(&FactoryResetService::handleRequest, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||||
|
|
||||||
ESP_LOGV("FactoryResetService", "Registered POST endpoint: %s", FACTORY_RESET_SERVICE_PATH);
|
ESP_LOGV("FactoryResetService", "Registered POST endpoint: %s", FACTORY_RESET_SERVICE_PATH);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void FeaturesService::begin()
|
|||||||
{
|
{
|
||||||
_server->on(FEATURES_SERVICE_PATH, HTTP_GET, [&](PsychicRequest *request)
|
_server->on(FEATURES_SERVICE_PATH, HTTP_GET, [&](PsychicRequest *request)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_FEATURES_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
|
|
||||||
#if FT_ENABLED(FT_SECURITY)
|
#if FT_ENABLED(FT_SECURITY)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <PsychicHttp.h>
|
#include <PsychicHttp.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define MAX_FEATURES_SIZE 256
|
|
||||||
#define FEATURES_SERVICE_PATH "/api/features"
|
#define FEATURES_SERVICE_PATH "/api/features"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
#ifndef HttpEndpoint_h
|
#ifndef HttpEndpoint_h
|
||||||
#define HttpEndpoint_h
|
#define HttpEndpoint_h
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <PsychicHttp.h>
|
#include <PsychicHttp.h>
|
||||||
|
|
||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
#include <StatefulService.h>
|
#include <StatefulService.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#define HTTP_ENDPOINT_ORIGIN_ID "http"
|
#define HTTP_ENDPOINT_ORIGIN_ID "http"
|
||||||
#define HTTPS_ENDPOINT_ORIGIN_ID "https"
|
#define HTTPS_ENDPOINT_ORIGIN_ID "https"
|
||||||
|
|
||||||
using namespace std::placeholders; // for `_1` etc
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class HttpEndpoint
|
class HttpEndpoint {
|
||||||
{
|
protected:
|
||||||
protected:
|
|
||||||
JsonStateReader<T> _stateReader;
|
JsonStateReader<T> _stateReader;
|
||||||
JsonStateUpdater<T> _stateUpdater;
|
JsonStateUpdater<T> _stateUpdater;
|
||||||
StatefulService<T> *_statefulService;
|
StatefulService<T> *_statefulService;
|
||||||
@@ -26,42 +22,38 @@ protected:
|
|||||||
PsychicHttpServer *_server;
|
PsychicHttpServer *_server;
|
||||||
const char *_servicePath;
|
const char *_servicePath;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HttpEndpoint(JsonStateReader<T> stateReader,
|
HttpEndpoint(JsonStateReader<T> stateReader,
|
||||||
JsonStateUpdater<T> stateUpdater,
|
JsonStateUpdater<T> stateUpdater,
|
||||||
StatefulService<T> *statefulService,
|
StatefulService<T> *statefulService, PsychicHttpServer *server,
|
||||||
PsychicHttpServer *server,
|
const char *servicePath, SecurityManager *securityManager,
|
||||||
const char *servicePath,
|
AuthenticationPredicate authenticationPredicate =
|
||||||
SecurityManager *securityManager,
|
AuthenticationPredicates::IS_ADMIN)
|
||||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
: _stateReader(stateReader),
|
||||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
_stateUpdater(stateUpdater),
|
||||||
: _stateReader(stateReader), _stateUpdater(stateUpdater), _statefulService(statefulService), _server(server), _servicePath(servicePath), _securityManager(securityManager), _authenticationPredicate(authenticationPredicate), _bufferSize(bufferSize)
|
_statefulService(statefulService),
|
||||||
{
|
_server(server),
|
||||||
}
|
_servicePath(servicePath),
|
||||||
|
_securityManager(securityManager),
|
||||||
|
_authenticationPredicate(authenticationPredicate) {}
|
||||||
|
|
||||||
// register the web server on() endpoints
|
// register the web server on() endpoints
|
||||||
void begin()
|
void begin() {
|
||||||
{
|
|
||||||
|
|
||||||
// OPTIONS (for CORS preflight)
|
// OPTIONS (for CORS preflight)
|
||||||
#ifdef ENABLE_CORS
|
#ifdef ENABLE_CORS
|
||||||
_server->on(_servicePath,
|
_server->on(
|
||||||
HTTP_OPTIONS,
|
_servicePath, HTTP_OPTIONS,
|
||||||
_securityManager->wrapRequest(
|
_securityManager->wrapRequest(
|
||||||
[this](PsychicRequest *request)
|
[this](PsychicRequest *request) { return request->reply(200); },
|
||||||
{
|
|
||||||
return request->reply(200);
|
|
||||||
},
|
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
_server->on(_servicePath,
|
_server->on(_servicePath, HTTP_GET,
|
||||||
HTTP_GET,
|
|
||||||
_securityManager->wrapRequest(
|
_securityManager->wrapRequest(
|
||||||
[this](PsychicRequest *request)
|
[this](PsychicRequest *request) {
|
||||||
{
|
PsychicJsonResponse response =
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, _bufferSize);
|
PsychicJsonResponse(request, false);
|
||||||
JsonObject jsonObject = response.getRoot();
|
JsonObject jsonObject = response.getRoot();
|
||||||
_statefulService->read(jsonObject, _stateReader);
|
_statefulService->read(jsonObject, _stateReader);
|
||||||
return response.send();
|
return response.send();
|
||||||
@@ -70,30 +62,29 @@ public:
|
|||||||
ESP_LOGV("HttpEndpoint", "Registered GET endpoint: %s", _servicePath);
|
ESP_LOGV("HttpEndpoint", "Registered GET endpoint: %s", _servicePath);
|
||||||
|
|
||||||
// POST
|
// POST
|
||||||
_server->on(_servicePath,
|
_server->on(
|
||||||
HTTP_POST,
|
_servicePath, HTTP_POST,
|
||||||
_securityManager->wrapCallback(
|
_securityManager->wrapCallback(
|
||||||
[this](PsychicRequest *request, JsonVariant &json)
|
[this](PsychicRequest *request, JsonVariant &json) {
|
||||||
{
|
if (!json.is<JsonObject>()) {
|
||||||
if (!json.is<JsonObject>())
|
|
||||||
{
|
|
||||||
return request->reply(400);
|
return request->reply(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject jsonObject = json.as<JsonObject>();
|
JsonObject jsonObject = json.as<JsonObject>();
|
||||||
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
StateUpdateResult outcome =
|
||||||
|
_statefulService->updateWithoutPropagation(
|
||||||
|
jsonObject, _stateUpdater);
|
||||||
|
|
||||||
if (outcome == StateUpdateResult::ERROR)
|
if (outcome == StateUpdateResult::ERROR) {
|
||||||
{
|
|
||||||
return request->reply(400);
|
return request->reply(400);
|
||||||
}
|
} else if ((outcome == StateUpdateResult::CHANGED)) {
|
||||||
else if ((outcome == StateUpdateResult::CHANGED))
|
|
||||||
{
|
|
||||||
// persist the changes to the FS
|
// persist the changes to the FS
|
||||||
_statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID);
|
_statefulService->callUpdateHandlers(
|
||||||
|
HTTP_ENDPOINT_ORIGIN_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, _bufferSize);
|
PsychicJsonResponse response =
|
||||||
|
PsychicJsonResponse(request, false);
|
||||||
jsonObject = response.getRoot();
|
jsonObject = response.getRoot();
|
||||||
|
|
||||||
_statefulService->read(jsonObject, _stateReader);
|
_statefulService->read(jsonObject, _stateReader);
|
||||||
|
|||||||
@@ -15,23 +15,20 @@
|
|||||||
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#include <StatefulService.h>
|
|
||||||
#include <PsychicMqttClient.h>
|
#include <PsychicMqttClient.h>
|
||||||
|
#include <StatefulService.h>
|
||||||
|
|
||||||
#define MQTT_ORIGIN_ID "mqtt"
|
#define MQTT_ORIGIN_ID "mqtt"
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class MqttEndpoint
|
class MqttEndpoint {
|
||||||
{
|
public:
|
||||||
public:
|
|
||||||
MqttEndpoint(JsonStateReader<T> stateReader,
|
MqttEndpoint(JsonStateReader<T> stateReader,
|
||||||
JsonStateUpdater<T> stateUpdater,
|
JsonStateUpdater<T> stateUpdater,
|
||||||
StatefulService<T> *statefulService,
|
StatefulService<T> *statefulService,
|
||||||
PsychicMqttClient *mqttClient,
|
PsychicMqttClient *mqttClient, const String &pubTopic = "",
|
||||||
const String &pubTopic = "",
|
const String &subTopic = "", bool retain = false)
|
||||||
const String &subTopic = "",
|
: _stateReader(stateReader),
|
||||||
bool retain = false,
|
|
||||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
|
||||||
_stateUpdater(stateUpdater),
|
_stateUpdater(stateUpdater),
|
||||||
_statefulService(statefulService),
|
_statefulService(statefulService),
|
||||||
_mqttClient(mqttClient),
|
_mqttClient(mqttClient),
|
||||||
@@ -41,35 +38,27 @@ public:
|
|||||||
_bufferSize(bufferSize)
|
_bufferSize(bufferSize)
|
||||||
|
|
||||||
{
|
{
|
||||||
_statefulService->addUpdateHandler([&](const String &originId)
|
_statefulService->addUpdateHandler(
|
||||||
{ publish(); },
|
[&](const String &originId) { publish(); }, false);
|
||||||
false);
|
|
||||||
|
|
||||||
_mqttClient->onConnect(std::bind(&MqttEndpoint::onConnect, this));
|
_mqttClient->onConnect(std::bind(&MqttEndpoint::onConnect, this));
|
||||||
|
|
||||||
_mqttClient->onMessage(std::bind(&MqttEndpoint::onMqttMessage,
|
_mqttClient->onMessage(
|
||||||
this,
|
std::bind(&MqttEndpoint::onMqttMessage, this, std::placeholders::_1,
|
||||||
std::placeholders::_1,
|
std::placeholders::_2, std::placeholders::_3,
|
||||||
std::placeholders::_2,
|
std::placeholders::_4, std::placeholders::_5));
|
||||||
std::placeholders::_3,
|
|
||||||
std::placeholders::_4,
|
|
||||||
std::placeholders::_5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void configureTopics(const String &pubTopic, const String &subTopic)
|
void configureTopics(const String &pubTopic, const String &subTopic) {
|
||||||
{
|
|
||||||
setSubTopic(subTopic);
|
setSubTopic(subTopic);
|
||||||
setPubTopic(pubTopic);
|
setPubTopic(pubTopic);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSubTopic(const String &subTopic)
|
void setSubTopic(const String &subTopic) {
|
||||||
{
|
if (!_subTopic.equals(subTopic)) {
|
||||||
if (!_subTopic.equals(subTopic))
|
|
||||||
{
|
|
||||||
// unsubscribe from the existing topic if one was set
|
// unsubscribe from the existing topic if one was set
|
||||||
if (_subTopic.length() > 0)
|
if (_subTopic.length() > 0) {
|
||||||
{
|
|
||||||
_mqttClient->unsubscribe(_subTopic.c_str());
|
_mqttClient->unsubscribe(_subTopic.c_str());
|
||||||
}
|
}
|
||||||
// set the new topic and re-configure the subscription
|
// set the new topic and re-configure the subscription
|
||||||
@@ -78,24 +67,20 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPubTopic(const String &pubTopic)
|
void setPubTopic(const String &pubTopic) {
|
||||||
{
|
|
||||||
_pubTopic = pubTopic;
|
_pubTopic = pubTopic;
|
||||||
publish();
|
publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRetain(const bool retain)
|
void setRetain(const bool retain) {
|
||||||
{
|
|
||||||
_retain = retain;
|
_retain = retain;
|
||||||
publish();
|
publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void publish()
|
void publish() {
|
||||||
{
|
if (_pubTopic.length() > 0 && _mqttClient->connected()) {
|
||||||
if (_pubTopic.length() > 0 && _mqttClient->connected())
|
|
||||||
{
|
|
||||||
// serialize to json doc
|
// serialize to json doc
|
||||||
DynamicJsonDocument json(_bufferSize);
|
JsonDocument json;
|
||||||
JsonObject jsonObject = json.to<JsonObject>();
|
JsonObject jsonObject = json.to<JsonObject>();
|
||||||
_statefulService->read(jsonObject, _stateReader);
|
_statefulService->read(jsonObject, _stateReader);
|
||||||
|
|
||||||
@@ -104,57 +89,45 @@ public:
|
|||||||
serializeJson(json, payload);
|
serializeJson(json, payload);
|
||||||
|
|
||||||
// publish the payload
|
// publish the payload
|
||||||
_mqttClient->publish(_pubTopic.c_str(), 0, _retain, payload.c_str());
|
_mqttClient->publish(_pubTopic.c_str(), 0, _retain,
|
||||||
|
payload.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicMqttClient *getMqttClient()
|
PsychicMqttClient *getMqttClient() { return _mqttClient; }
|
||||||
{
|
|
||||||
return _mqttClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
StatefulService<T> *_statefulService;
|
StatefulService<T> *_statefulService;
|
||||||
PsychicMqttClient *_mqttClient;
|
PsychicMqttClient *_mqttClient;
|
||||||
int _bufferSize;
|
|
||||||
JsonStateUpdater<T> _stateUpdater;
|
JsonStateUpdater<T> _stateUpdater;
|
||||||
JsonStateReader<T> _stateReader;
|
JsonStateReader<T> _stateReader;
|
||||||
String _subTopic;
|
String _subTopic;
|
||||||
String _pubTopic;
|
String _pubTopic;
|
||||||
bool _retain;
|
bool _retain;
|
||||||
|
|
||||||
void onMqttMessage(char *topic,
|
void onMqttMessage(char *topic, char *payload, int retain, int qos,
|
||||||
char *payload,
|
bool dup) {
|
||||||
int retain,
|
|
||||||
int qos,
|
|
||||||
bool dup)
|
|
||||||
{
|
|
||||||
// we only care about the topic we are watching in this class
|
// we only care about the topic we are watching in this class
|
||||||
if (strcmp(_subTopic.c_str(), topic))
|
if (strcmp(_subTopic.c_str(), topic)) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// deserialize from string
|
// deserialize from string
|
||||||
DynamicJsonDocument json(_bufferSize);
|
JsonDocument json;
|
||||||
DeserializationError error = deserializeJson(json, payload);
|
DeserializationError error = deserializeJson(json, payload);
|
||||||
if (!error && json.is<JsonObject>())
|
if (!error && json.is<JsonObject>()) {
|
||||||
{
|
|
||||||
JsonObject jsonObject = json.as<JsonObject>();
|
JsonObject jsonObject = json.as<JsonObject>();
|
||||||
_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID);
|
_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onConnect()
|
void onConnect() {
|
||||||
{
|
|
||||||
subscribe();
|
subscribe();
|
||||||
publish();
|
publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void subscribe()
|
void subscribe() {
|
||||||
{
|
if (_subTopic.length() > 0) {
|
||||||
if (_subTopic.length() > 0)
|
|
||||||
{
|
|
||||||
_mqttClient->subscribe(_subTopic.c_str(), 2);
|
_mqttClient->subscribe(_subTopic.c_str(), 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public:
|
|||||||
if (_pubTopic.length() > 0 && _mqttClient->connected())
|
if (_pubTopic.length() > 0 && _mqttClient->connected())
|
||||||
{
|
{
|
||||||
// serialize to json doc
|
// serialize to json doc
|
||||||
DynamicJsonDocument json(_bufferSize);
|
JsonDocument json;
|
||||||
JsonObject jsonObject = json.to<JsonObject>();
|
JsonObject jsonObject = json.to<JsonObject>();
|
||||||
_statefulService->read(jsonObject, _stateReader);
|
_statefulService->read(jsonObject, _stateReader);
|
||||||
|
|
||||||
@@ -116,7 +116,6 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
StatefulService<T> *_statefulService;
|
StatefulService<T> *_statefulService;
|
||||||
PsychicMqttClient *_mqttClient;
|
PsychicMqttClient *_mqttClient;
|
||||||
int _bufferSize;
|
|
||||||
JsonStateUpdater<T> _stateUpdater;
|
JsonStateUpdater<T> _stateUpdater;
|
||||||
JsonStateReader<T> _stateReader;
|
JsonStateReader<T> _stateReader;
|
||||||
String _subTopic;
|
String _subTopic;
|
||||||
@@ -136,7 +135,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deserialize from string
|
// deserialize from string
|
||||||
DynamicJsonDocument json(_bufferSize);
|
JsonDocument json;
|
||||||
DeserializationError error = deserializeJson(json, payload);
|
DeserializationError error = deserializeJson(json, payload);
|
||||||
if (!error && json.is<JsonObject>())
|
if (!error && json.is<JsonObject>())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ void MqttStatus::begin()
|
|||||||
|
|
||||||
esp_err_t MqttStatus::mqttStatus(PsychicRequest *request)
|
esp_err_t MqttStatus::mqttStatus(PsychicRequest *request)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_MQTT_STATUS_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
|
|
||||||
root["enabled"] = _mqttSettingsService->isEnabled();
|
root["enabled"] = _mqttSettingsService->isEnabled();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <PsychicHttp.h>
|
#include <PsychicHttp.h>
|
||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
|
|
||||||
#define MAX_MQTT_STATUS_SIZE 1024
|
|
||||||
#define MQTT_STATUS_SERVICE_PATH "/api/mqttStatus"
|
#define MQTT_STATUS_SERVICE_PATH "/api/mqttStatus"
|
||||||
|
|
||||||
class MqttStatus
|
class MqttStatus
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
||||||
#define NTP_SETTINGS_SERVICE_PATH "/api/ntpSettings"
|
#define NTP_SETTINGS_SERVICE_PATH "/api/ntpSettings"
|
||||||
|
|
||||||
#define MAX_TIME_SIZE 256
|
|
||||||
#define TIME_PATH "/api/time"
|
#define TIME_PATH "/api/time"
|
||||||
|
|
||||||
class NTPSettings
|
class NTPSettings
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ String toLocalTimeString(tm *time)
|
|||||||
|
|
||||||
esp_err_t NTPStatus::ntpStatus(PsychicRequest *request)
|
esp_err_t NTPStatus::ntpStatus(PsychicRequest *request)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_NTP_STATUS_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
|
|
||||||
// grab the current instant in unix seconds
|
// grab the current instant in unix seconds
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#include <PsychicHttp.h>
|
#include <PsychicHttp.h>
|
||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
|
|
||||||
#define MAX_NTP_STATUS_SIZE 1024
|
|
||||||
#define NTP_STATUS_SERVICE_PATH "/api/ntpStatus"
|
#define NTP_STATUS_SERVICE_PATH "/api/ntpStatus"
|
||||||
|
|
||||||
class NTPStatus
|
class NTPStatus
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ void SecuritySettingsService::configureJWTHandler()
|
|||||||
|
|
||||||
Authentication SecuritySettingsService::authenticateJWT(String &jwt)
|
Authentication SecuritySettingsService::authenticateJWT(String &jwt)
|
||||||
{
|
{
|
||||||
DynamicJsonDocument payloadDocument(MAX_JWT_SIZE);
|
JsonDocument payloadDocument;
|
||||||
_jwtHandler.parseJWT(jwt, payloadDocument);
|
_jwtHandler.parseJWT(jwt, payloadDocument);
|
||||||
if (payloadDocument.is<JsonObject>())
|
if (payloadDocument.is<JsonObject>())
|
||||||
{
|
{
|
||||||
@@ -106,7 +106,7 @@ inline void populateJWTPayload(JsonObject &payload, User *user)
|
|||||||
|
|
||||||
boolean SecuritySettingsService::validatePayload(JsonObject &parsedPayload, User *user)
|
boolean SecuritySettingsService::validatePayload(JsonObject &parsedPayload, User *user)
|
||||||
{
|
{
|
||||||
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
|
JsonDocument jsonDocument;
|
||||||
JsonObject payload = jsonDocument.to<JsonObject>();
|
JsonObject payload = jsonDocument.to<JsonObject>();
|
||||||
populateJWTPayload(payload, user);
|
populateJWTPayload(payload, user);
|
||||||
return payload == parsedPayload;
|
return payload == parsedPayload;
|
||||||
@@ -114,7 +114,7 @@ boolean SecuritySettingsService::validatePayload(JsonObject &parsedPayload, User
|
|||||||
|
|
||||||
String SecuritySettingsService::generateJWT(User *user)
|
String SecuritySettingsService::generateJWT(User *user)
|
||||||
{
|
{
|
||||||
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
|
JsonDocument jsonDocument;
|
||||||
JsonObject payload = jsonDocument.to<JsonObject>();
|
JsonObject payload = jsonDocument.to<JsonObject>();
|
||||||
populateJWTPayload(payload, user);
|
populateJWTPayload(payload, user);
|
||||||
return _jwtHandler.buildJWT(payload);
|
return _jwtHandler.buildJWT(payload);
|
||||||
@@ -179,7 +179,7 @@ esp_err_t SecuritySettingsService::generateToken(PsychicRequest *request)
|
|||||||
{
|
{
|
||||||
if (_user.username == usernameParam)
|
if (_user.username == usernameParam)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, GENERATE_TOKEN_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
root["token"] = generateJWT(&_user);
|
root["token"] = generateJWT(&_user);
|
||||||
return response.send();
|
return response.send();
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
||||||
#define SECURITY_SETTINGS_PATH "/api/securitySettings"
|
#define SECURITY_SETTINGS_PATH "/api/securitySettings"
|
||||||
|
|
||||||
#define GENERATE_TOKEN_SIZE 512
|
|
||||||
#define GENERATE_TOKEN_PATH "/api/generateToken"
|
#define GENERATE_TOKEN_PATH "/api/generateToken"
|
||||||
|
|
||||||
#if FT_ENABLED(FT_SECURITY)
|
#if FT_ENABLED(FT_SECURITY)
|
||||||
@@ -61,10 +60,10 @@ public:
|
|||||||
root["jwt_secret"] = settings.jwtSecret;
|
root["jwt_secret"] = settings.jwtSecret;
|
||||||
|
|
||||||
// users
|
// users
|
||||||
JsonArray users = root.createNestedArray("users");
|
JsonArray users = root.to<JsonArray>();
|
||||||
for (User user : settings.users)
|
for (User user : settings.users)
|
||||||
{
|
{
|
||||||
JsonObject userRoot = users.createNestedObject();
|
JsonObject userRoot = users.add<JsonObject>();
|
||||||
userRoot["username"] = user.username;
|
userRoot["username"] = user.username;
|
||||||
userRoot["password"] = user.password;
|
userRoot["password"] = user.password;
|
||||||
userRoot["admin"] = user.admin;
|
userRoot["admin"] = user.admin;
|
||||||
|
|||||||
@@ -23,10 +23,6 @@
|
|||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
#ifndef DEFAULT_BUFFER_SIZE
|
|
||||||
#define DEFAULT_BUFFER_SIZE 1024
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum class StateUpdateResult
|
enum class StateUpdateResult
|
||||||
{
|
{
|
||||||
CHANGED = 0, // The update changed the state and propagation should take place if required
|
CHANGED = 0, // The update changed the state and propagation should take place if required
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ void SystemStatus::begin()
|
|||||||
|
|
||||||
esp_err_t SystemStatus::systemStatus(PsychicRequest *request)
|
esp_err_t SystemStatus::systemStatus(PsychicRequest *request)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_ESP_STATUS_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
|
|
||||||
root["esp_platform"] = ESP_PLATFORM;
|
root["esp_platform"] = ESP_PLATFORM;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
#include <ESPFS.h>
|
#include <ESPFS.h>
|
||||||
|
|
||||||
#define MAX_ESP_STATUS_SIZE 1024
|
|
||||||
#define SYSTEM_STATUS_SERVICE_PATH "/api/systemStatus"
|
#define SYSTEM_STATUS_SERVICE_PATH "/api/systemStatus"
|
||||||
|
|
||||||
class SystemStatus
|
class SystemStatus
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include <esp_ota_ops.h>
|
#include <esp_ota_ops.h>
|
||||||
#include <esp_app_format.h>
|
#include <esp_app_format.h>
|
||||||
|
|
||||||
using namespace std::placeholders; // for `_1` etc
|
using namespace std::placeholders;
|
||||||
|
|
||||||
static char md5[33] = "\0";
|
static char md5[33] = "\0";
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ esp_err_t UploadFirmwareService::uploadComplete(PsychicRequest *request)
|
|||||||
{
|
{
|
||||||
if (strlen(md5) == 32)
|
if (strlen(md5) == 32)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, 256);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
root["md5"] = md5;
|
root["md5"] = md5;
|
||||||
return response.send();
|
return response.send();
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
/**
|
/**
|
||||||
* ESP32 SvelteKit
|
* ESP32 SvelteKit
|
||||||
*
|
*
|
||||||
* A simple, secure and extensible framework for IoT projects for ESP32 platforms
|
* A simple, secure and extensible framework for IoT projects for ESP32
|
||||||
* with responsive Sveltekit front-end built with TailwindCSS and DaisyUI.
|
*platforms with responsive Sveltekit front-end built with TailwindCSS and
|
||||||
* https://github.com/theelims/ESP32-sveltekit
|
*DaisyUI. https://github.com/theelims/ESP32-sveltekit
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018 - 2023 rjwats
|
* Copyright (C) 2018 - 2023 rjwats
|
||||||
* Copyright (C) 2023 theelims
|
* Copyright (C) 2023 theelims
|
||||||
@@ -15,9 +15,9 @@
|
|||||||
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#include <StatefulService.h>
|
|
||||||
#include <PsychicHttp.h>
|
#include <PsychicHttp.h>
|
||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
|
#include <StatefulService.h>
|
||||||
|
|
||||||
#define WEB_SOCKET_CLIENT_ID_MSG_SIZE 128
|
#define WEB_SOCKET_CLIENT_ID_MSG_SIZE 128
|
||||||
|
|
||||||
@@ -25,90 +25,86 @@
|
|||||||
#define WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX "wsserver:"
|
#define WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX "wsserver:"
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class WebSocketServer
|
class WebSocketServer {
|
||||||
{
|
public:
|
||||||
public:
|
|
||||||
WebSocketServer(JsonStateReader<T> stateReader,
|
WebSocketServer(JsonStateReader<T> stateReader,
|
||||||
JsonStateUpdater<T> stateUpdater,
|
JsonStateUpdater<T> stateUpdater,
|
||||||
StatefulService<T> *statefulService,
|
StatefulService<T> *statefulService,
|
||||||
PsychicHttpServer *server,
|
PsychicHttpServer *server, const char *webSocketPath,
|
||||||
const char *webSocketPath,
|
|
||||||
SecurityManager *securityManager,
|
SecurityManager *securityManager,
|
||||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
AuthenticationPredicate authenticationPredicate =
|
||||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
AuthenticationPredicates::IS_ADMIN)
|
||||||
|
: _stateReader(stateReader),
|
||||||
_stateUpdater(stateUpdater),
|
_stateUpdater(stateUpdater),
|
||||||
_statefulService(statefulService),
|
_statefulService(statefulService),
|
||||||
_server(server),
|
_server(server),
|
||||||
_bufferSize(bufferSize),
|
|
||||||
_webSocketPath(webSocketPath),
|
_webSocketPath(webSocketPath),
|
||||||
_authenticationPredicate(authenticationPredicate),
|
_authenticationPredicate(authenticationPredicate),
|
||||||
_securityManager(securityManager)
|
_securityManager(securityManager) {
|
||||||
{
|
|
||||||
_statefulService->addUpdateHandler(
|
_statefulService->addUpdateHandler(
|
||||||
[&](const String &originId)
|
[&](const String &originId) { transmitData(nullptr, originId); },
|
||||||
{ transmitData(nullptr, originId); },
|
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void begin()
|
void begin() {
|
||||||
{
|
_webSocket.setFilter(
|
||||||
_webSocket.setFilter(_securityManager->filterRequest(_authenticationPredicate));
|
_securityManager->filterRequest(_authenticationPredicate));
|
||||||
_webSocket.onOpen(std::bind(&WebSocketServer::onWSOpen,
|
_webSocket.onOpen(
|
||||||
this,
|
std::bind(&WebSocketServer::onWSOpen, this, std::placeholders::_1));
|
||||||
|
_webSocket.onClose(std::bind(&WebSocketServer::onWSClose, this,
|
||||||
std::placeholders::_1));
|
std::placeholders::_1));
|
||||||
_webSocket.onClose(std::bind(&WebSocketServer::onWSClose,
|
_webSocket.onFrame(std::bind(&WebSocketServer::onWSFrame, this,
|
||||||
this,
|
|
||||||
std::placeholders::_1));
|
|
||||||
_webSocket.onFrame(std::bind(&WebSocketServer::onWSFrame,
|
|
||||||
this,
|
|
||||||
std::placeholders::_1,
|
std::placeholders::_1,
|
||||||
std::placeholders::_2));
|
std::placeholders::_2));
|
||||||
_server->on(_webSocketPath.c_str(), &_webSocket);
|
_server->on(_webSocketPath.c_str(), &_webSocket);
|
||||||
|
|
||||||
ESP_LOGV("WebSocketServer", "Registered WebSocket handler: %s", _webSocketPath.c_str());
|
ESP_LOGV("WebSocketServer", "Registered WebSocket handler: %s",
|
||||||
|
_webSocketPath.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onWSOpen(PsychicWebSocketClient *client)
|
void onWSOpen(PsychicWebSocketClient *client) {
|
||||||
{
|
|
||||||
|
|
||||||
// when a client connects, we transmit it's id and the current payload
|
// when a client connects, we transmit it's id and the current payload
|
||||||
transmitId(client);
|
transmitId(client);
|
||||||
transmitData(client, WEB_SOCKET_ORIGIN);
|
transmitData(client, WEB_SOCKET_ORIGIN);
|
||||||
ESP_LOGI("WebSocketServer", "ws[%s][%u] connect", client->remoteIP().toString().c_str(), client->socket());
|
ESP_LOGI("WebSocketServer", "ws[%s][%u] connect",
|
||||||
|
client->remoteIP().toString().c_str(), client->socket());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onWSClose(PsychicWebSocketClient *client)
|
void onWSClose(PsychicWebSocketClient *client) {
|
||||||
{
|
ESP_LOGI("WebSocketServer", "ws[%s][%u] disconnect",
|
||||||
ESP_LOGI("WebSocketServer", "ws[%s][%u] disconnect", client->remoteIP().toString().c_str(), client->socket());
|
client->remoteIP().toString().c_str(), client->socket());
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t onWSFrame(PsychicWebSocketRequest *request, httpd_ws_frame *frame)
|
esp_err_t onWSFrame(PsychicWebSocketRequest *request,
|
||||||
{
|
httpd_ws_frame *frame) {
|
||||||
ESP_LOGV("WebSocketServer", "ws[%s][%u] opcode[%d]", request->client()->remoteIP().toString().c_str(), request->client()->socket(), frame->type);
|
ESP_LOGV("WebSocketServer", "ws[%s][%u] opcode[%d]",
|
||||||
|
request->client()->remoteIP().toString().c_str(),
|
||||||
|
request->client()->socket(), frame->type);
|
||||||
|
|
||||||
if (frame->type == HTTPD_WS_TYPE_TEXT)
|
if (frame->type == HTTPD_WS_TYPE_TEXT) {
|
||||||
{
|
ESP_LOGV("WebSocketServer", "ws[%s][%u] request: %s",
|
||||||
ESP_LOGV("WebSocketServer", "ws[%s][%u] request: %s", request->client()->remoteIP().toString().c_str(), request->client()->socket(), (char *)frame->payload);
|
request->client()->remoteIP().toString().c_str(),
|
||||||
|
request->client()->socket(), (char *)frame->payload);
|
||||||
|
|
||||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
JsonDocument jsonDocument;
|
||||||
DeserializationError error = deserializeJson(jsonDocument, (char *)frame->payload, frame->len);
|
DeserializationError error = deserializeJson(
|
||||||
|
jsonDocument, (char *)frame->payload, frame->len);
|
||||||
|
|
||||||
if (!error && jsonDocument.is<JsonObject>())
|
if (!error && jsonDocument.is<JsonObject>()) {
|
||||||
{
|
|
||||||
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
||||||
_statefulService->update(jsonObject, _stateUpdater, clientId(request->client()));
|
_statefulService->update(jsonObject, _stateUpdater,
|
||||||
|
clientId(request->client()));
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
String clientId(PsychicWebSocketClient *client)
|
String clientId(PsychicWebSocketClient *client) {
|
||||||
{
|
|
||||||
return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->socket());
|
return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->socket());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JsonStateReader<T> _stateReader;
|
JsonStateReader<T> _stateReader;
|
||||||
JsonStateUpdater<T> _stateUpdater;
|
JsonStateUpdater<T> _stateUpdater;
|
||||||
StatefulService<T> *_statefulService;
|
StatefulService<T> *_statefulService;
|
||||||
@@ -119,9 +115,8 @@ private:
|
|||||||
String _webSocketPath;
|
String _webSocketPath;
|
||||||
size_t _bufferSize;
|
size_t _bufferSize;
|
||||||
|
|
||||||
void transmitId(PsychicWebSocketClient *client)
|
void transmitId(PsychicWebSocketClient *client) {
|
||||||
{
|
JsonDocument jsonDocument;
|
||||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WEB_SOCKET_CLIENT_ID_MSG_SIZE);
|
|
||||||
JsonObject root = jsonDocument.to<JsonObject>();
|
JsonObject root = jsonDocument.to<JsonObject>();
|
||||||
root["type"] = "id";
|
root["type"] = "id";
|
||||||
root["id"] = clientId(client);
|
root["id"] = clientId(client);
|
||||||
@@ -133,15 +128,15 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if
|
* Broadcasts the payload to the destination, if provided. Otherwise
|
||||||
* specified.
|
* broadcasts to all clients except the origin, if specified.
|
||||||
*
|
*
|
||||||
* Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach
|
* Original implementation sent clients their own IDs so they could ignore
|
||||||
* simplifies the client and the server implementation but may not be sufficient for all use-cases.
|
* updates they initiated. This approach simplifies the client and the
|
||||||
|
* server implementation but may not be sufficient for all use-cases.
|
||||||
*/
|
*/
|
||||||
void transmitData(PsychicWebSocketClient *client, const String &originId)
|
void transmitData(PsychicWebSocketClient *client, const String &originId) {
|
||||||
{
|
JsonDocument jsonDocument;
|
||||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
|
||||||
JsonObject root = jsonDocument.to<JsonObject>();
|
JsonObject root = jsonDocument.to<JsonObject>();
|
||||||
String buffer;
|
String buffer;
|
||||||
|
|
||||||
@@ -149,12 +144,9 @@ private:
|
|||||||
|
|
||||||
// serialize the json to a string
|
// serialize the json to a string
|
||||||
serializeJson(jsonDocument, buffer);
|
serializeJson(jsonDocument, buffer);
|
||||||
if (client)
|
if (client) {
|
||||||
{
|
|
||||||
client->sendMessage(buffer.c_str());
|
client->sendMessage(buffer.c_str());
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
_webSocket.sendAll(buffer.c_str());
|
_webSocket.sendAll(buffer.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ esp_err_t WiFiScanner::listNetworks(PsychicRequest *request)
|
|||||||
int numNetworks = WiFi.scanComplete();
|
int numNetworks = WiFi.scanComplete();
|
||||||
if (numNetworks > -1)
|
if (numNetworks > -1)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_WIFI_SCANNER_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
JsonArray networks = root.createNestedArray("networks");
|
JsonArray networks = root["networks"].to<JsonArray>();
|
||||||
for (int i = 0; i < numNetworks; i++)
|
for (int i = 0; i < numNetworks; i++)
|
||||||
{
|
{
|
||||||
JsonObject network = networks.createNestedObject();
|
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);
|
||||||
|
|||||||
@@ -24,8 +24,6 @@
|
|||||||
#define SCAN_NETWORKS_SERVICE_PATH "/api/scanNetworks"
|
#define SCAN_NETWORKS_SERVICE_PATH "/api/scanNetworks"
|
||||||
#define LIST_NETWORKS_SERVICE_PATH "/api/listNetworks"
|
#define LIST_NETWORKS_SERVICE_PATH "/api/listNetworks"
|
||||||
|
|
||||||
#define MAX_WIFI_SCANNER_SIZE 1024
|
|
||||||
|
|
||||||
class WiFiScanner
|
class WiFiScanner
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ WiFiSettingsService::WiFiSettingsService(PsychicHttpServer *server, FS *fs, Secu
|
|||||||
EventSocket *socket)
|
EventSocket *socket)
|
||||||
: _server(server), _securityManager(securityManager),
|
: _server(server), _securityManager(securityManager),
|
||||||
_httpEndpoint(WiFiSettings::read, WiFiSettings::update, this, server, WIFI_SETTINGS_SERVICE_PATH, securityManager,
|
_httpEndpoint(WiFiSettings::read, WiFiSettings::update, this, server, WIFI_SETTINGS_SERVICE_PATH, securityManager,
|
||||||
AuthenticationPredicates::IS_ADMIN, WIFI_SETTINGS_BUFFER_SIZE),
|
AuthenticationPredicates::IS_ADMIN),
|
||||||
_eventEndpoint(WiFiSettings::read, WiFiSettings::update, this, socket, EVENT_WIFI_SETTINGS,
|
_eventEndpoint(WiFiSettings::read, WiFiSettings::update, this, socket, EVENT_WIFI_SETTINGS),
|
||||||
WIFI_SETTINGS_BUFFER_SIZE),
|
|
||||||
_fsPersistence(WiFiSettings::read, WiFiSettings::update, this, fs, WIFI_SETTINGS_FILE), _lastConnectionAttempt(0),
|
_fsPersistence(WiFiSettings::read, WiFiSettings::update, this, fs, WIFI_SETTINGS_FILE), _lastConnectionAttempt(0),
|
||||||
_socket(socket)
|
_socket(socket)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,8 +50,6 @@
|
|||||||
#define WIFI_RECONNECTION_DELAY 1000 * 30
|
#define WIFI_RECONNECTION_DELAY 1000 * 30
|
||||||
#define RSSI_EVENT_DELAY 500
|
#define RSSI_EVENT_DELAY 500
|
||||||
|
|
||||||
#define WIFI_SETTINGS_BUFFER_SIZE 2048
|
|
||||||
|
|
||||||
#define EVENT_RSSI "rssi"
|
#define EVENT_RSSI "rssi"
|
||||||
#define EVENT_WIFI_SETTINGS "WiFiSettings"
|
#define EVENT_WIFI_SETTINGS "WiFiSettings"
|
||||||
|
|
||||||
@@ -83,13 +81,13 @@ public:
|
|||||||
root["priority_RSSI"] = settings.priorityBySignalStrength;
|
root["priority_RSSI"] = settings.priorityBySignalStrength;
|
||||||
|
|
||||||
// create JSON array from root
|
// create JSON array from root
|
||||||
JsonArray wifiNetworks = root.createNestedArray("wifi_networks");
|
JsonArray wifiNetworks = root["wifi_networks"].to<JsonArray>();
|
||||||
|
|
||||||
// iterate over the wifiSettings
|
// iterate over the wifiSettings
|
||||||
for (auto &wifi : settings.wifiSettings)
|
for (auto &wifi : settings.wifiSettings)
|
||||||
{
|
{
|
||||||
// create JSON object for each wifi network
|
// create JSON object for each wifi network
|
||||||
JsonObject wifiNetwork = wifiNetworks.createNestedObject();
|
JsonObject wifiNetwork = wifiNetworks.add<JsonObject>();
|
||||||
|
|
||||||
// add the ssid and password to the JSON object
|
// add the ssid and password to the JSON object
|
||||||
wifiNetwork["ssid"] = wifi.ssid;
|
wifiNetwork["ssid"] = wifi.ssid;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
|
|||||||
|
|
||||||
esp_err_t WiFiStatus::wifiStatus(PsychicRequest *request)
|
esp_err_t WiFiStatus::wifiStatus(PsychicRequest *request)
|
||||||
{
|
{
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_WIFI_STATUS_SIZE);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
wl_status_t status = WiFi.status();
|
wl_status_t status = WiFi.status();
|
||||||
root["status"] = (uint8_t)status;
|
root["status"] = (uint8_t)status;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <IPUtils.h>
|
#include <IPUtils.h>
|
||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
|
|
||||||
#define MAX_WIFI_STATUS_SIZE 1024
|
|
||||||
#define WIFI_STATUS_SERVICE_PATH "/api/wifiStatus"
|
#define WIFI_STATUS_SERVICE_PATH "/api/wifiStatus"
|
||||||
|
|
||||||
class WiFiStatus
|
class WiFiStatus
|
||||||
|
|||||||
@@ -36,20 +36,20 @@ build_unflags = -std=gnu++11
|
|||||||
test_ignore = test_embedded
|
test_ignore = test_embedded
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson@^6.21.2
|
ArduinoJson@>=7.0.0
|
||||||
https://github.com/theelims/PsychicMqttClient.git#0.1.1
|
https://github.com/theelims/PsychicMqttClient.git#0.1.1
|
||||||
; thomasfredericks/Bounce2@ ^2.7.0
|
|
||||||
teckel12/NewPing@^1.9.7
|
teckel12/NewPing@^1.9.7
|
||||||
adafruit/Adafruit SSD1306@^2.5.7
|
adafruit/Adafruit SSD1306@^2.5.7
|
||||||
adafruit/Adafruit GFX Library@^1.11.5
|
adafruit/Adafruit GFX Library@^1.11.5
|
||||||
adafruit/Adafruit BusIO@^1.9.3
|
adafruit/Adafruit BusIO@^1.9.3
|
||||||
|
rfetick/MPU6050_light@^1.1.0
|
||||||
|
SPI
|
||||||
|
; thomasfredericks/Bounce2@ ^2.7.0
|
||||||
; adafruit/Adafruit PWM Servo Driver Library@^2.4.1
|
; adafruit/Adafruit PWM Servo Driver Library@^2.4.1
|
||||||
; adafruit/Adafruit ADS1X15@^2.4.0
|
; adafruit/Adafruit ADS1X15@^2.4.0
|
||||||
; adafruit/Adafruit HMC5883 Unified@^1.2.1
|
; adafruit/Adafruit HMC5883 Unified@^1.2.1
|
||||||
; adafruit/Adafruit Unified Sensor@^1.1.11
|
; adafruit/Adafruit Unified Sensor@^1.1.11
|
||||||
; plageoj/UrlEncode@ ^1.0.1
|
; plageoj/UrlEncode@ ^1.0.1
|
||||||
rfetick/MPU6050_light@^1.1.0
|
|
||||||
SPI
|
|
||||||
; board_build.partitions = config/no_oat.csv
|
; board_build.partitions = config/no_oat.csv
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:scripts/build_app.py
|
pre:scripts/build_app.py
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
/**
|
|
||||||
* ESP32 SvelteKit
|
|
||||||
*
|
|
||||||
* A simple, secure and extensible framework for IoT projects for ESP32 platforms
|
|
||||||
* with responsive Sveltekit front-end built with TailwindCSS and DaisyUI.
|
|
||||||
* https://github.com/theelims/ESP32-sveltekit
|
|
||||||
*
|
|
||||||
* Copyright (C) 2018 - 2023 rjwats
|
|
||||||
* Copyright (C) 2023 theelims
|
|
||||||
*
|
|
||||||
* All Rights Reserved. This software may be modified and distributed under
|
|
||||||
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
|
||||||
**/
|
|
||||||
|
|
||||||
#include <ActuatorStateService.h>
|
|
||||||
|
|
||||||
ActuatorStateService::ActuatorStateService(
|
|
||||||
PsychicHttpServer *server,
|
|
||||||
SecurityManager *securityManager
|
|
||||||
) : _httpEndpoint(
|
|
||||||
ActuatorState::read,
|
|
||||||
ActuatorState::update,
|
|
||||||
this,
|
|
||||||
server,
|
|
||||||
ACTUATOR_SETTINGS_ENDPOINT_PATH,
|
|
||||||
securityManager,
|
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED),
|
|
||||||
_webSocketServer(
|
|
||||||
ActuatorState::read,
|
|
||||||
ActuatorState::update,
|
|
||||||
this,
|
|
||||||
server,
|
|
||||||
ACTUATOR_SETTINGS_SOCKET_PATH,
|
|
||||||
securityManager,
|
|
||||||
AuthenticationPredicates::IS_AUTHENTICATED)
|
|
||||||
{
|
|
||||||
// Setup actuator hardware
|
|
||||||
|
|
||||||
addUpdateHandler([&](const String &originId){ onConfigUpdated(); }, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActuatorStateService::begin()
|
|
||||||
{
|
|
||||||
_httpEndpoint.begin();
|
|
||||||
_webSocketServer.begin();
|
|
||||||
onConfigUpdated();
|
|
||||||
xTaskCreatePinnedToCore(
|
|
||||||
this->_loopImpl, // Function that should be called
|
|
||||||
"Motion Service", // Name of the task (for debugging)
|
|
||||||
4096, // Stack size (bytes)
|
|
||||||
this, // Pass reference to this class instance
|
|
||||||
(tskIDLE_PRIORITY + 1), // task priority
|
|
||||||
NULL, // Task handle
|
|
||||||
ESP32SVELTEKIT_RUNNING_CORE // Pin to application core
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActuatorStateService::onConfigUpdated()
|
|
||||||
{
|
|
||||||
log_i("Update hardware interface for actuators");
|
|
||||||
// UPDATE HARDWARE INTERFACE FOR ACTUATORS
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
#ifndef LightStateService_h
|
|
||||||
#define LightStateService_h
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ESP32 SvelteKit
|
|
||||||
*
|
|
||||||
* A simple, secure and extensible framework for IoT projects for ESP32 platforms
|
|
||||||
* with responsive Sveltekit front-end built with TailwindCSS and DaisyUI.
|
|
||||||
* https://github.com/theelims/ESP32-sveltekit
|
|
||||||
*
|
|
||||||
* Copyright (C) 2018 - 2023 rjwats
|
|
||||||
* Copyright (C) 2023 theelims
|
|
||||||
*
|
|
||||||
* All Rights Reserved. This software may be modified and distributed under
|
|
||||||
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
|
||||||
**/
|
|
||||||
|
|
||||||
#include <HttpEndpoint.h>
|
|
||||||
#include <WebSocketServer.h>
|
|
||||||
|
|
||||||
#define ACTUATOR_SETTINGS_ENDPOINT_PATH "/rest/actuators"
|
|
||||||
#define ACTUATOR_SETTINGS_SOCKET_PATH "/ws"
|
|
||||||
|
|
||||||
#define MOTION_INTERVAL 100
|
|
||||||
|
|
||||||
#define MAX_ESP_ANGLE_SIZE 256
|
|
||||||
|
|
||||||
enum class MOTION_STATE
|
|
||||||
{
|
|
||||||
IDLE,
|
|
||||||
REST,
|
|
||||||
STAND,
|
|
||||||
WALK
|
|
||||||
};
|
|
||||||
|
|
||||||
class ActuatorState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int16_t angles[12] = {0, 45, -90, 0, 45, -90, 0, 45, -90, 0, 45, -90};
|
|
||||||
int8_t controller[7] = {0, 0, 0, 0, 0, 0 ,0};
|
|
||||||
MOTION_STATE motionState = MOTION_STATE::IDLE;
|
|
||||||
|
|
||||||
static void read(ActuatorState &settings, JsonObject &root)
|
|
||||||
{
|
|
||||||
root["type"] = "angles";
|
|
||||||
JsonArray array = root.createNestedArray("data");
|
|
||||||
for(int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
array.add(settings.angles[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static StateUpdateResult update(JsonObject &root, ActuatorState &actuatorState)
|
|
||||||
{
|
|
||||||
if (root["type"] == "mode") {
|
|
||||||
if (actuatorState.motionState == (MOTION_STATE)root["data"].as<int>()) return StateUpdateResult::UNCHANGED;
|
|
||||||
actuatorState.motionState = (MOTION_STATE)root["data"].as<int>();
|
|
||||||
return StateUpdateResult::UNCHANGED;
|
|
||||||
}
|
|
||||||
if (root["type"] != "angles") return StateUpdateResult::UNCHANGED;
|
|
||||||
JsonArray array = root["data"];
|
|
||||||
bool changed = false;
|
|
||||||
for(int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
if (actuatorState.angles[i] != array[i].as<int16_t>())
|
|
||||||
{
|
|
||||||
actuatorState.angles[i] = array[i];
|
|
||||||
//changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return changed ? StateUpdateResult::CHANGED : StateUpdateResult::UNCHANGED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void homeAssistRead(ActuatorState &settings, JsonObject &root)
|
|
||||||
{
|
|
||||||
JsonArray array = root.createNestedArray("angles");
|
|
||||||
for(int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
array.add(settings.angles[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static StateUpdateResult homeAssistUpdate(JsonObject &root, ActuatorState &actuatorState)
|
|
||||||
{
|
|
||||||
JsonArray array = root["angles"];
|
|
||||||
if(array.size() != 12) return StateUpdateResult::ERROR;
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
for(int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
if (actuatorState.angles[i] != array[i].as<int16_t>())
|
|
||||||
{
|
|
||||||
actuatorState.angles[i] = array[i];
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return changed ? StateUpdateResult::CHANGED : StateUpdateResult::UNCHANGED;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ActuatorStateService : public StatefulService<ActuatorState> {
|
|
||||||
public:
|
|
||||||
ActuatorStateService(PsychicHttpServer *server, SecurityManager *securityManager);
|
|
||||||
void begin();
|
|
||||||
protected:
|
|
||||||
static void _loopImpl(void *_this) { static_cast<ActuatorStateService *>(_this)->_loop(); }
|
|
||||||
void _loop()
|
|
||||||
{
|
|
||||||
TickType_t xLastWakeTime;
|
|
||||||
xLastWakeTime = xTaskGetTickCount();
|
|
||||||
int dir = 2;
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
_state.angles[1] += dir;
|
|
||||||
if (_state.angles[1] >= 90) dir = -1;
|
|
||||||
if (_state.angles[1] <= 0) dir = 1;
|
|
||||||
StaticJsonDocument<MAX_ESP_ANGLE_SIZE> doc;
|
|
||||||
String message;
|
|
||||||
JsonArray array = doc.createNestedArray("angles");
|
|
||||||
for(int16_t num : _state.angles) {
|
|
||||||
array.add(num);
|
|
||||||
}
|
|
||||||
doc["mode"] = (int)_state.motionState;
|
|
||||||
|
|
||||||
serializeJson(doc, message);
|
|
||||||
// _notificationEvents->send(message, "motion", millis());
|
|
||||||
vTaskDelayUntil(&xLastWakeTime, MOTION_INTERVAL / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
HttpEndpoint<ActuatorState> _httpEndpoint;
|
|
||||||
WebSocketServer<ActuatorState> _webSocketServer;
|
|
||||||
|
|
||||||
void onConfigUpdated();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user