Upgrades ArduinoJson from version 6 to 7

This commit is contained in:
Rune Harlyk
2024-05-07 10:36:16 +02:00
committed by Rune Harlyk
parent 2b4d196e7c
commit a150caad9d
39 changed files with 241 additions and 561 deletions
+1 -1
View File
@@ -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();
-1
View File
@@ -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
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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()
{ {
+1 -1
View File
@@ -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();
+2 -5
View File
@@ -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);
+2 -32
View File
@@ -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])
-11
View File
@@ -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);
+40 -51
View File
@@ -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
+37 -46
View File
@@ -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);
+34 -61
View File
@@ -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);
} }
} }
+2 -3
View File
@@ -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>())
{ {
+1 -1
View File
@@ -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();
-1
View File
@@ -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
+1 -1
View File
@@ -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
-1
View File
@@ -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
+1 -1
View File
@@ -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;
-1
View File
@@ -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();
+55 -63
View File
@@ -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());
} }
} }
+3 -3
View File
@@ -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);
-2
View File
@@ -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;
+1 -1
View File
@@ -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;
-1
View File
@@ -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
+4 -4
View File
@@ -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
-62
View File
@@ -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
}
-137
View File
@@ -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