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)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_AP_STATUS_SIZE);
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
root["status"] = _apSettingsService->getAPNetworkStatus();
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <SecurityManager.h>
|
||||
#include <APSettingsService.h>
|
||||
|
||||
#define MAX_AP_STATUS_SIZE 1024
|
||||
#define AP_STATUS_SERVICE_PATH "/api/apStatus"
|
||||
|
||||
class APStatus
|
||||
|
||||
@@ -43,7 +43,7 @@ class AnalyticsService
|
||||
updateAnalytics();
|
||||
}
|
||||
};
|
||||
StaticJsonDocument<MAX_ESP_ANALYTICS_SIZE> doc;
|
||||
JsonDocument doc;
|
||||
char message[MAX_ESP_ANALYTICS_SIZE];
|
||||
|
||||
private:
|
||||
@@ -65,10 +65,10 @@ class AnalyticsService
|
||||
doc["cpu1_usage"] = _taskManager->getCpuUsage(1);
|
||||
doc["cpu_usage"] = _taskManager->getCpuUsage();
|
||||
// Add _taskManager->getTaskNames() as a JSON array
|
||||
JsonArray tasks = doc.createNestedArray("tasks");
|
||||
JsonArray tasks = doc["tasks"].as<JsonArray>();
|
||||
for (auto const &task : _taskManager->getTasks())
|
||||
{
|
||||
JsonObject nested = tasks.createNestedObject();
|
||||
JsonObject nested = tasks.add<JsonObject>();
|
||||
nested["name"] = task.name;
|
||||
nested["stackSize"] = task.stackSize;
|
||||
nested["priority"] = task.priority;
|
||||
|
||||
@@ -27,13 +27,13 @@ void AuthenticationService::begin()
|
||||
_server->on(SIGN_IN_PATH, HTTP_POST, [this](PsychicRequest *request, JsonVariant &json)
|
||||
{
|
||||
if (json.is<JsonObject>()) {
|
||||
String username = json["username"];
|
||||
String password = json["password"];
|
||||
String username = json["username"];
|
||||
String password = json["password"];
|
||||
Authentication authentication = _securityManager->authenticate(username, password);
|
||||
if (authentication.authenticated) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, 256);
|
||||
JsonObject root = response.getRoot();
|
||||
root["access_token"] = _securityManager->generateJWT(authentication.user);
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||
JsonObject root = response.getRoot();
|
||||
root["access_token"] = _securityManager->generateJWT(authentication.user);
|
||||
return response.send();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#define VERIFY_AUTHORIZATION_PATH "/api/verifyAuthorization"
|
||||
#define SIGN_IN_PATH "/api/signIn"
|
||||
|
||||
#define MAX_AUTHENTICATION_SIZE 256
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
class AuthenticationService
|
||||
|
||||
@@ -24,7 +24,7 @@ void BatteryService::begin()
|
||||
|
||||
void BatteryService::batteryEvent()
|
||||
{
|
||||
StaticJsonDocument<32> doc;
|
||||
JsonDocument doc;
|
||||
char message[32];
|
||||
doc["soc"] = _lastSOC;
|
||||
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 int previousProgress = 0;
|
||||
StaticJsonDocument<128> doc;
|
||||
JsonDocument doc;
|
||||
|
||||
void update_started()
|
||||
{
|
||||
|
||||
@@ -59,7 +59,7 @@ ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server, unsigned int numberEnd
|
||||
|
||||
void ESP32SvelteKit::begin() {
|
||||
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);
|
||||
|
||||
_wifiSettingsService.initWiFi();
|
||||
|
||||
@@ -27,12 +27,10 @@ public:
|
||||
EventEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> *statefulService,
|
||||
EventSocket *socket, const char *event,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
||||
EventSocket *socket, const char *event) : _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_socket(socket),
|
||||
_bufferSize(bufferSize),
|
||||
_event(event)
|
||||
{
|
||||
_statefulService->addUpdateHandler([&](const String &originId)
|
||||
@@ -53,7 +51,6 @@ private:
|
||||
StatefulService<T> *_statefulService;
|
||||
EventSocket *_socket;
|
||||
const char *_event;
|
||||
size_t _bufferSize;
|
||||
|
||||
void updateState(JsonObject &root, int originId)
|
||||
{
|
||||
@@ -62,7 +59,7 @@ private:
|
||||
|
||||
void syncState(const String &originId, bool sync = false)
|
||||
{
|
||||
DynamicJsonDocument jsonDocument{_bufferSize};
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject root = jsonDocument.to<JsonObject>();
|
||||
String output;
|
||||
_statefulService->read(root, _stateReader);
|
||||
|
||||
@@ -6,8 +6,7 @@ EventSocket::EventSocket(PsychicHttpServer *server,
|
||||
SecurityManager *securityManager,
|
||||
AuthenticationPredicate authenticationPredicate) : _server(server),
|
||||
_securityManager(securityManager),
|
||||
_authenticationPredicate(authenticationPredicate),
|
||||
_bufferSize(1024)
|
||||
_authenticationPredicate(authenticationPredicate)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -19,11 +18,6 @@ void EventSocket::begin()
|
||||
_socket.onFrame(std::bind(&EventSocket::onFrame, this, std::placeholders::_1, std::placeholders::_2));
|
||||
_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);
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
request->client()->socket(), (char *)frame->payload);
|
||||
|
||||
DynamicJsonDocument doc = DynamicJsonDocument(_bufferSize);
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, (char *)frame->payload, frame->len);
|
||||
|
||||
if (!error && doc.is<JsonObject>())
|
||||
@@ -159,30 +153,6 @@ void EventSocket::emit(const char *event, const char *payload, const char *origi
|
||||
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)
|
||||
{
|
||||
for (auto &callback : event_callbacks[event])
|
||||
|
||||
@@ -13,14 +13,6 @@
|
||||
typedef std::function<void(JsonObject &root, int originId)> EventCallback;
|
||||
typedef std::function<void(const String &originId, bool sync)> SubscribeCallback;
|
||||
|
||||
enum pushEvent
|
||||
{
|
||||
PUSHERROR,
|
||||
PUSHWARNING,
|
||||
PUSHINFO,
|
||||
PUSHSUCCESS
|
||||
};
|
||||
|
||||
class EventSocket
|
||||
{
|
||||
public:
|
||||
@@ -41,8 +33,6 @@ public:
|
||||
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
|
||||
|
||||
void pushNotification(String message, pushEvent event);
|
||||
|
||||
private:
|
||||
PsychicHttpServer *_server;
|
||||
PsychicWebSocketHandler _socket;
|
||||
@@ -58,7 +48,6 @@ private:
|
||||
|
||||
bool isEventValid(String event);
|
||||
|
||||
size_t _bufferSize;
|
||||
void onWSOpen(PsychicWebSocketClient *client);
|
||||
void onWSClose(PsychicWebSocketClient *client);
|
||||
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.
|
||||
**/
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <FS.h>
|
||||
#include <StatefulService.h>
|
||||
|
||||
template <class T>
|
||||
class FSPersistence
|
||||
{
|
||||
public:
|
||||
class FSPersistence {
|
||||
public:
|
||||
FSPersistence(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> *statefulService,
|
||||
FS *fs,
|
||||
const char *filePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_fs(fs),
|
||||
_filePath(filePath),
|
||||
_bufferSize(bufferSize),
|
||||
_updateHandlerId(0)
|
||||
{
|
||||
StatefulService<T> *statefulService, FS *fs,
|
||||
const char *filePath)
|
||||
: _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_fs(fs),
|
||||
_filePath(filePath),
|
||||
_updateHandlerId(0) {
|
||||
enableUpdateHandler();
|
||||
}
|
||||
|
||||
void readFromFS()
|
||||
{
|
||||
void readFromFS() {
|
||||
File settingsFile = _fs->open(_filePath, "r");
|
||||
|
||||
if (settingsFile)
|
||||
{
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
||||
DeserializationError error = deserializeJson(jsonDocument, settingsFile);
|
||||
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>())
|
||||
{
|
||||
if (settingsFile) {
|
||||
JsonDocument jsonDocument;
|
||||
DeserializationError error =
|
||||
deserializeJson(jsonDocument, settingsFile);
|
||||
if (error == DeserializationError::Ok &&
|
||||
jsonDocument.is<JsonObject>()) {
|
||||
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
||||
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||
_statefulService->updateWithoutPropagation(jsonObject,
|
||||
_stateUpdater);
|
||||
settingsFile.close();
|
||||
return;
|
||||
}
|
||||
settingsFile.close();
|
||||
}
|
||||
|
||||
// If we reach here we have not been successful in loading the config and hard-coded defaults are now applied.
|
||||
// The settings are then 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.
|
||||
// If we reach here we have not been successful in loading the config
|
||||
// and hard-coded defaults are now applied. The settings are then
|
||||
// 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();
|
||||
writeToFS();
|
||||
}
|
||||
|
||||
bool writeToFS()
|
||||
{
|
||||
bool writeToFS() {
|
||||
// create and populate a new json object
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject jsonObject = jsonDocument.to<JsonObject>();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
|
||||
@@ -77,8 +74,7 @@ public:
|
||||
File settingsFile = _fs->open(_filePath, "w");
|
||||
|
||||
// failed to open file, return false
|
||||
if (!settingsFile)
|
||||
{
|
||||
if (!settingsFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,25 +84,21 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
void disableUpdateHandler()
|
||||
{
|
||||
if (_updateHandlerId)
|
||||
{
|
||||
void disableUpdateHandler() {
|
||||
if (_updateHandlerId) {
|
||||
_statefulService->removeUpdateHandler(_updateHandlerId);
|
||||
_updateHandlerId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void enableUpdateHandler()
|
||||
{
|
||||
if (!_updateHandlerId)
|
||||
{
|
||||
_updateHandlerId = _statefulService->addUpdateHandler([&](const String &originId)
|
||||
{ writeToFS(); });
|
||||
void enableUpdateHandler() {
|
||||
if (!_updateHandlerId) {
|
||||
_updateHandlerId = _statefulService->addUpdateHandler(
|
||||
[&](const String &originId) { writeToFS(); });
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
JsonStateReader<T> _stateReader;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T> *_statefulService;
|
||||
@@ -115,31 +107,28 @@ private:
|
||||
size_t _bufferSize;
|
||||
update_handler_id_t _updateHandlerId;
|
||||
|
||||
// We assume we have a _filePath with format "/directory1/directory2/filename"
|
||||
// We create a directory for each missing parent
|
||||
void mkdirs()
|
||||
{
|
||||
// We assume we have a _filePath with format
|
||||
// "/directory1/directory2/filename" We create a directory for each missing
|
||||
// parent
|
||||
void mkdirs() {
|
||||
String path(_filePath);
|
||||
int index = 0;
|
||||
while ((index = path.indexOf('/', index + 1)) != -1)
|
||||
{
|
||||
while ((index = path.indexOf('/', index + 1)) != -1) {
|
||||
String segment = path.substring(0, index);
|
||||
if (!_fs->exists(segment))
|
||||
{
|
||||
if (!_fs->exists(segment)) {
|
||||
_fs->mkdir(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// We assume the updater supplies sensible defaults if an empty object
|
||||
// is supplied, this virtual function allows that to be changed.
|
||||
virtual void applyDefaults()
|
||||
{
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
||||
virtual void applyDefaults() {
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
||||
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // end FSPersistence
|
||||
#endif // end FSPersistence
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include <FactoryResetService.h>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
FactoryResetService::FactoryResetService(PsychicHttpServer *server,
|
||||
FS *fs,
|
||||
SecurityManager *securityManager) : _server(server),
|
||||
@@ -28,7 +26,7 @@ void FactoryResetService::begin()
|
||||
{
|
||||
_server->on(FACTORY_RESET_SERVICE_PATH,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ void FeaturesService::begin()
|
||||
{
|
||||
_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();
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <PsychicHttp.h>
|
||||
#include <vector>
|
||||
|
||||
#define MAX_FEATURES_SIZE 256
|
||||
#define FEATURES_SERVICE_PATH "/api/features"
|
||||
|
||||
typedef struct
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
#ifndef HttpEndpoint_h
|
||||
#define HttpEndpoint_h
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <PsychicHttp.h>
|
||||
|
||||
#include <SecurityManager.h>
|
||||
#include <StatefulService.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define HTTP_ENDPOINT_ORIGIN_ID "http"
|
||||
#define HTTPS_ENDPOINT_ORIGIN_ID "https"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
template <class T>
|
||||
class HttpEndpoint
|
||||
{
|
||||
protected:
|
||||
class HttpEndpoint {
|
||||
protected:
|
||||
JsonStateReader<T> _stateReader;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T> *_statefulService;
|
||||
@@ -26,42 +22,38 @@ protected:
|
||||
PsychicHttpServer *_server;
|
||||
const char *_servicePath;
|
||||
|
||||
public:
|
||||
public:
|
||||
HttpEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> *statefulService,
|
||||
PsychicHttpServer *server,
|
||||
const char *servicePath,
|
||||
SecurityManager *securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: _stateReader(stateReader), _stateUpdater(stateUpdater), _statefulService(statefulService), _server(server), _servicePath(servicePath), _securityManager(securityManager), _authenticationPredicate(authenticationPredicate), _bufferSize(bufferSize)
|
||||
{
|
||||
}
|
||||
StatefulService<T> *statefulService, PsychicHttpServer *server,
|
||||
const char *servicePath, SecurityManager *securityManager,
|
||||
AuthenticationPredicate authenticationPredicate =
|
||||
AuthenticationPredicates::IS_ADMIN)
|
||||
: _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_server(server),
|
||||
_servicePath(servicePath),
|
||||
_securityManager(securityManager),
|
||||
_authenticationPredicate(authenticationPredicate) {}
|
||||
|
||||
// register the web server on() endpoints
|
||||
void begin()
|
||||
{
|
||||
|
||||
void begin() {
|
||||
// OPTIONS (for CORS preflight)
|
||||
#ifdef ENABLE_CORS
|
||||
_server->on(_servicePath,
|
||||
HTTP_OPTIONS,
|
||||
_securityManager->wrapRequest(
|
||||
[this](PsychicRequest *request)
|
||||
{
|
||||
return request->reply(200);
|
||||
},
|
||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
_server->on(
|
||||
_servicePath, HTTP_OPTIONS,
|
||||
_securityManager->wrapRequest(
|
||||
[this](PsychicRequest *request) { return request->reply(200); },
|
||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
#endif
|
||||
|
||||
// GET
|
||||
_server->on(_servicePath,
|
||||
HTTP_GET,
|
||||
_server->on(_servicePath, HTTP_GET,
|
||||
_securityManager->wrapRequest(
|
||||
[this](PsychicRequest *request)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, _bufferSize);
|
||||
[this](PsychicRequest *request) {
|
||||
PsychicJsonResponse response =
|
||||
PsychicJsonResponse(request, false);
|
||||
JsonObject jsonObject = response.getRoot();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
return response.send();
|
||||
@@ -70,37 +62,36 @@ public:
|
||||
ESP_LOGV("HttpEndpoint", "Registered GET endpoint: %s", _servicePath);
|
||||
|
||||
// POST
|
||||
_server->on(_servicePath,
|
||||
HTTP_POST,
|
||||
_securityManager->wrapCallback(
|
||||
[this](PsychicRequest *request, JsonVariant &json)
|
||||
{
|
||||
if (!json.is<JsonObject>())
|
||||
{
|
||||
return request->reply(400);
|
||||
}
|
||||
_server->on(
|
||||
_servicePath, HTTP_POST,
|
||||
_securityManager->wrapCallback(
|
||||
[this](PsychicRequest *request, JsonVariant &json) {
|
||||
if (!json.is<JsonObject>()) {
|
||||
return request->reply(400);
|
||||
}
|
||||
|
||||
JsonObject jsonObject = json.as<JsonObject>();
|
||||
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||
JsonObject jsonObject = json.as<JsonObject>();
|
||||
StateUpdateResult outcome =
|
||||
_statefulService->updateWithoutPropagation(
|
||||
jsonObject, _stateUpdater);
|
||||
|
||||
if (outcome == StateUpdateResult::ERROR)
|
||||
{
|
||||
return request->reply(400);
|
||||
}
|
||||
else if ((outcome == StateUpdateResult::CHANGED))
|
||||
{
|
||||
// persist the changes to the FS
|
||||
_statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID);
|
||||
}
|
||||
if (outcome == StateUpdateResult::ERROR) {
|
||||
return request->reply(400);
|
||||
} else if ((outcome == StateUpdateResult::CHANGED)) {
|
||||
// persist the changes to the FS
|
||||
_statefulService->callUpdateHandlers(
|
||||
HTTP_ENDPOINT_ORIGIN_ID);
|
||||
}
|
||||
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, _bufferSize);
|
||||
jsonObject = response.getRoot();
|
||||
PsychicJsonResponse response =
|
||||
PsychicJsonResponse(request, false);
|
||||
jsonObject = response.getRoot();
|
||||
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
|
||||
return response.send();
|
||||
},
|
||||
_authenticationPredicate));
|
||||
return response.send();
|
||||
},
|
||||
_authenticationPredicate));
|
||||
|
||||
ESP_LOGV("HttpEndpoint", "Registered POST endpoint: %s", _servicePath);
|
||||
}
|
||||
|
||||
@@ -15,61 +15,50 @@
|
||||
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
||||
**/
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <PsychicMqttClient.h>
|
||||
#include <StatefulService.h>
|
||||
|
||||
#define MQTT_ORIGIN_ID "mqtt"
|
||||
|
||||
template <class T>
|
||||
class MqttEndpoint
|
||||
{
|
||||
public:
|
||||
class MqttEndpoint {
|
||||
public:
|
||||
MqttEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> *statefulService,
|
||||
PsychicMqttClient *mqttClient,
|
||||
const String &pubTopic = "",
|
||||
const String &subTopic = "",
|
||||
bool retain = false,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_mqttClient(mqttClient),
|
||||
_pubTopic(pubTopic),
|
||||
_subTopic(subTopic),
|
||||
_retain(retain),
|
||||
_bufferSize(bufferSize)
|
||||
PsychicMqttClient *mqttClient, const String &pubTopic = "",
|
||||
const String &subTopic = "", bool retain = false)
|
||||
: _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_mqttClient(mqttClient),
|
||||
_pubTopic(pubTopic),
|
||||
_subTopic(subTopic),
|
||||
_retain(retain),
|
||||
_bufferSize(bufferSize)
|
||||
|
||||
{
|
||||
_statefulService->addUpdateHandler([&](const String &originId)
|
||||
{ publish(); },
|
||||
false);
|
||||
_statefulService->addUpdateHandler(
|
||||
[&](const String &originId) { publish(); }, false);
|
||||
|
||||
_mqttClient->onConnect(std::bind(&MqttEndpoint::onConnect, this));
|
||||
|
||||
_mqttClient->onMessage(std::bind(&MqttEndpoint::onMqttMessage,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3,
|
||||
std::placeholders::_4,
|
||||
std::placeholders::_5));
|
||||
_mqttClient->onMessage(
|
||||
std::bind(&MqttEndpoint::onMqttMessage, this, std::placeholders::_1,
|
||||
std::placeholders::_2, std::placeholders::_3,
|
||||
std::placeholders::_4, std::placeholders::_5));
|
||||
}
|
||||
|
||||
public:
|
||||
void configureTopics(const String &pubTopic, const String &subTopic)
|
||||
{
|
||||
public:
|
||||
void configureTopics(const String &pubTopic, const String &subTopic) {
|
||||
setSubTopic(subTopic);
|
||||
setPubTopic(pubTopic);
|
||||
}
|
||||
|
||||
void setSubTopic(const String &subTopic)
|
||||
{
|
||||
if (!_subTopic.equals(subTopic))
|
||||
{
|
||||
void setSubTopic(const String &subTopic) {
|
||||
if (!_subTopic.equals(subTopic)) {
|
||||
// unsubscribe from the existing topic if one was set
|
||||
if (_subTopic.length() > 0)
|
||||
{
|
||||
if (_subTopic.length() > 0) {
|
||||
_mqttClient->unsubscribe(_subTopic.c_str());
|
||||
}
|
||||
// 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;
|
||||
publish();
|
||||
}
|
||||
|
||||
void setRetain(const bool retain)
|
||||
{
|
||||
void setRetain(const bool retain) {
|
||||
_retain = retain;
|
||||
publish();
|
||||
}
|
||||
|
||||
void publish()
|
||||
{
|
||||
if (_pubTopic.length() > 0 && _mqttClient->connected())
|
||||
{
|
||||
void publish() {
|
||||
if (_pubTopic.length() > 0 && _mqttClient->connected()) {
|
||||
// serialize to json doc
|
||||
DynamicJsonDocument json(_bufferSize);
|
||||
JsonDocument json;
|
||||
JsonObject jsonObject = json.to<JsonObject>();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
|
||||
@@ -104,60 +89,48 @@ public:
|
||||
serializeJson(json, 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()
|
||||
{
|
||||
return _mqttClient;
|
||||
}
|
||||
PsychicMqttClient *getMqttClient() { return _mqttClient; }
|
||||
|
||||
protected:
|
||||
protected:
|
||||
StatefulService<T> *_statefulService;
|
||||
PsychicMqttClient *_mqttClient;
|
||||
int _bufferSize;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
JsonStateReader<T> _stateReader;
|
||||
String _subTopic;
|
||||
String _pubTopic;
|
||||
bool _retain;
|
||||
|
||||
void onMqttMessage(char *topic,
|
||||
char *payload,
|
||||
int retain,
|
||||
int qos,
|
||||
bool dup)
|
||||
{
|
||||
void onMqttMessage(char *topic, char *payload, int retain, int qos,
|
||||
bool dup) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// deserialize from string
|
||||
DynamicJsonDocument json(_bufferSize);
|
||||
JsonDocument json;
|
||||
DeserializationError error = deserializeJson(json, payload);
|
||||
if (!error && json.is<JsonObject>())
|
||||
{
|
||||
if (!error && json.is<JsonObject>()) {
|
||||
JsonObject jsonObject = json.as<JsonObject>();
|
||||
_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID);
|
||||
}
|
||||
}
|
||||
|
||||
void onConnect()
|
||||
{
|
||||
void onConnect() {
|
||||
subscribe();
|
||||
publish();
|
||||
}
|
||||
|
||||
void subscribe()
|
||||
{
|
||||
if (_subTopic.length() > 0)
|
||||
{
|
||||
void subscribe() {
|
||||
if (_subTopic.length() > 0) {
|
||||
_mqttClient->subscribe(_subTopic.c_str(), 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // end MqttEndpoint
|
||||
#endif // end MqttEndpoint
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
if (_pubTopic.length() > 0 && _mqttClient->connected())
|
||||
{
|
||||
// serialize to json doc
|
||||
DynamicJsonDocument json(_bufferSize);
|
||||
JsonDocument json;
|
||||
JsonObject jsonObject = json.to<JsonObject>();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
|
||||
@@ -116,7 +116,6 @@ public:
|
||||
protected:
|
||||
StatefulService<T> *_statefulService;
|
||||
PsychicMqttClient *_mqttClient;
|
||||
int _bufferSize;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
JsonStateReader<T> _stateReader;
|
||||
String _subTopic;
|
||||
@@ -136,7 +135,7 @@ protected:
|
||||
}
|
||||
|
||||
// deserialize from string
|
||||
DynamicJsonDocument json(_bufferSize);
|
||||
JsonDocument json;
|
||||
DeserializationError error = deserializeJson(json, payload);
|
||||
if (!error && json.is<JsonObject>())
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@ void MqttStatus::begin()
|
||||
|
||||
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();
|
||||
|
||||
root["enabled"] = _mqttSettingsService->isEnabled();
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define MAX_MQTT_STATUS_SIZE 1024
|
||||
#define MQTT_STATUS_SERVICE_PATH "/api/mqttStatus"
|
||||
|
||||
class MqttStatus
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
||||
#define NTP_SETTINGS_SERVICE_PATH "/api/ntpSettings"
|
||||
|
||||
#define MAX_TIME_SIZE 256
|
||||
#define TIME_PATH "/api/time"
|
||||
|
||||
class NTPSettings
|
||||
|
||||
@@ -53,7 +53,7 @@ String toLocalTimeString(tm *time)
|
||||
|
||||
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();
|
||||
|
||||
// grab the current instant in unix seconds
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define MAX_NTP_STATUS_SIZE 1024
|
||||
#define NTP_STATUS_SERVICE_PATH "/api/ntpStatus"
|
||||
|
||||
class NTPStatus
|
||||
|
||||
@@ -69,7 +69,7 @@ void SecuritySettingsService::configureJWTHandler()
|
||||
|
||||
Authentication SecuritySettingsService::authenticateJWT(String &jwt)
|
||||
{
|
||||
DynamicJsonDocument payloadDocument(MAX_JWT_SIZE);
|
||||
JsonDocument payloadDocument;
|
||||
_jwtHandler.parseJWT(jwt, payloadDocument);
|
||||
if (payloadDocument.is<JsonObject>())
|
||||
{
|
||||
@@ -106,7 +106,7 @@ inline void populateJWTPayload(JsonObject &payload, User *user)
|
||||
|
||||
boolean SecuritySettingsService::validatePayload(JsonObject &parsedPayload, User *user)
|
||||
{
|
||||
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject payload = jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
return payload == parsedPayload;
|
||||
@@ -114,7 +114,7 @@ boolean SecuritySettingsService::validatePayload(JsonObject &parsedPayload, User
|
||||
|
||||
String SecuritySettingsService::generateJWT(User *user)
|
||||
{
|
||||
DynamicJsonDocument jsonDocument(MAX_JWT_SIZE);
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject payload = jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
return _jwtHandler.buildJWT(payload);
|
||||
@@ -179,7 +179,7 @@ esp_err_t SecuritySettingsService::generateToken(PsychicRequest *request)
|
||||
{
|
||||
if (_user.username == usernameParam)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, GENERATE_TOKEN_SIZE);
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||
JsonObject root = response.getRoot();
|
||||
root["token"] = generateJWT(&_user);
|
||||
return response.send();
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
||||
#define SECURITY_SETTINGS_PATH "/api/securitySettings"
|
||||
|
||||
#define GENERATE_TOKEN_SIZE 512
|
||||
#define GENERATE_TOKEN_PATH "/api/generateToken"
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
@@ -61,10 +60,10 @@ public:
|
||||
root["jwt_secret"] = settings.jwtSecret;
|
||||
|
||||
// users
|
||||
JsonArray users = root.createNestedArray("users");
|
||||
JsonArray users = root.to<JsonArray>();
|
||||
for (User user : settings.users)
|
||||
{
|
||||
JsonObject userRoot = users.createNestedObject();
|
||||
JsonObject userRoot = users.add<JsonObject>();
|
||||
userRoot["username"] = user.username;
|
||||
userRoot["password"] = user.password;
|
||||
userRoot["admin"] = user.admin;
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
#ifndef DEFAULT_BUFFER_SIZE
|
||||
#define DEFAULT_BUFFER_SIZE 1024
|
||||
#endif
|
||||
|
||||
enum class StateUpdateResult
|
||||
{
|
||||
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)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_ESP_STATUS_SIZE);
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
root["esp_platform"] = ESP_PLATFORM;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <SecurityManager.h>
|
||||
#include <ESPFS.h>
|
||||
|
||||
#define MAX_ESP_STATUS_SIZE 1024
|
||||
#define SYSTEM_STATUS_SERVICE_PATH "/api/systemStatus"
|
||||
|
||||
class SystemStatus
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_app_format.h>
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
using namespace std::placeholders;
|
||||
|
||||
static char md5[33] = "\0";
|
||||
|
||||
@@ -152,7 +152,7 @@ esp_err_t UploadFirmwareService::uploadComplete(PsychicRequest *request)
|
||||
{
|
||||
if (strlen(md5) == 32)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, 256);
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||
JsonObject root = response.getRoot();
|
||||
root["md5"] = md5;
|
||||
return response.send();
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
@@ -15,9 +15,9 @@
|
||||
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
||||
**/
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <StatefulService.h>
|
||||
|
||||
#define WEB_SOCKET_CLIENT_ID_MSG_SIZE 128
|
||||
|
||||
@@ -25,90 +25,86 @@
|
||||
#define WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX "wsserver:"
|
||||
|
||||
template <class T>
|
||||
class WebSocketServer
|
||||
{
|
||||
public:
|
||||
class WebSocketServer {
|
||||
public:
|
||||
WebSocketServer(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> *statefulService,
|
||||
PsychicHttpServer *server,
|
||||
const char *webSocketPath,
|
||||
PsychicHttpServer *server, const char *webSocketPath,
|
||||
SecurityManager *securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_server(server),
|
||||
_bufferSize(bufferSize),
|
||||
_webSocketPath(webSocketPath),
|
||||
_authenticationPredicate(authenticationPredicate),
|
||||
_securityManager(securityManager)
|
||||
{
|
||||
AuthenticationPredicate authenticationPredicate =
|
||||
AuthenticationPredicates::IS_ADMIN)
|
||||
: _stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
_server(server),
|
||||
_webSocketPath(webSocketPath),
|
||||
_authenticationPredicate(authenticationPredicate),
|
||||
_securityManager(securityManager) {
|
||||
_statefulService->addUpdateHandler(
|
||||
[&](const String &originId)
|
||||
{ transmitData(nullptr, originId); },
|
||||
[&](const String &originId) { transmitData(nullptr, originId); },
|
||||
false);
|
||||
}
|
||||
|
||||
void begin()
|
||||
{
|
||||
_webSocket.setFilter(_securityManager->filterRequest(_authenticationPredicate));
|
||||
_webSocket.onOpen(std::bind(&WebSocketServer::onWSOpen,
|
||||
this,
|
||||
std::placeholders::_1));
|
||||
_webSocket.onClose(std::bind(&WebSocketServer::onWSClose,
|
||||
this,
|
||||
void begin() {
|
||||
_webSocket.setFilter(
|
||||
_securityManager->filterRequest(_authenticationPredicate));
|
||||
_webSocket.onOpen(
|
||||
std::bind(&WebSocketServer::onWSOpen, this, std::placeholders::_1));
|
||||
_webSocket.onClose(std::bind(&WebSocketServer::onWSClose, this,
|
||||
std::placeholders::_1));
|
||||
_webSocket.onFrame(std::bind(&WebSocketServer::onWSFrame,
|
||||
this,
|
||||
_webSocket.onFrame(std::bind(&WebSocketServer::onWSFrame, this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
_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
|
||||
transmitId(client);
|
||||
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)
|
||||
{
|
||||
ESP_LOGI("WebSocketServer", "ws[%s][%u] disconnect", client->remoteIP().toString().c_str(), client->socket());
|
||||
void onWSClose(PsychicWebSocketClient *client) {
|
||||
ESP_LOGI("WebSocketServer", "ws[%s][%u] disconnect",
|
||||
client->remoteIP().toString().c_str(), client->socket());
|
||||
}
|
||||
|
||||
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_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);
|
||||
|
||||
if (frame->type == HTTPD_WS_TYPE_TEXT)
|
||||
{
|
||||
ESP_LOGV("WebSocketServer", "ws[%s][%u] request: %s", request->client()->remoteIP().toString().c_str(), request->client()->socket(), (char *)frame->payload);
|
||||
if (frame->type == HTTPD_WS_TYPE_TEXT) {
|
||||
ESP_LOGV("WebSocketServer", "ws[%s][%u] request: %s",
|
||||
request->client()->remoteIP().toString().c_str(),
|
||||
request->client()->socket(), (char *)frame->payload);
|
||||
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
||||
DeserializationError error = deserializeJson(jsonDocument, (char *)frame->payload, frame->len);
|
||||
JsonDocument jsonDocument;
|
||||
DeserializationError error = deserializeJson(
|
||||
jsonDocument, (char *)frame->payload, frame->len);
|
||||
|
||||
if (!error && jsonDocument.is<JsonObject>())
|
||||
{
|
||||
if (!error && jsonDocument.is<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;
|
||||
}
|
||||
|
||||
String clientId(PsychicWebSocketClient *client)
|
||||
{
|
||||
String clientId(PsychicWebSocketClient *client) {
|
||||
return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->socket());
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
JsonStateReader<T> _stateReader;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T> *_statefulService;
|
||||
@@ -119,9 +115,8 @@ private:
|
||||
String _webSocketPath;
|
||||
size_t _bufferSize;
|
||||
|
||||
void transmitId(PsychicWebSocketClient *client)
|
||||
{
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WEB_SOCKET_CLIENT_ID_MSG_SIZE);
|
||||
void transmitId(PsychicWebSocketClient *client) {
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject root = jsonDocument.to<JsonObject>();
|
||||
root["type"] = "id";
|
||||
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
|
||||
* specified.
|
||||
* Broadcasts the payload to the destination, if provided. Otherwise
|
||||
* 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
|
||||
* simplifies the client and the server implementation but may not be sufficient for all use-cases.
|
||||
* Original implementation sent clients their own IDs so they could ignore
|
||||
* updates they initiated. This approach simplifies the client and the
|
||||
* server implementation but may not be sufficient for all use-cases.
|
||||
*/
|
||||
void transmitData(PsychicWebSocketClient *client, const String &originId)
|
||||
{
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
||||
void transmitData(PsychicWebSocketClient *client, const String &originId) {
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject root = jsonDocument.to<JsonObject>();
|
||||
String buffer;
|
||||
|
||||
@@ -149,12 +144,9 @@ private:
|
||||
|
||||
// serialize the json to a string
|
||||
serializeJson(jsonDocument, buffer);
|
||||
if (client)
|
||||
{
|
||||
if (client) {
|
||||
client->sendMessage(buffer.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
_webSocket.sendAll(buffer.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ esp_err_t WiFiScanner::listNetworks(PsychicRequest *request)
|
||||
int numNetworks = WiFi.scanComplete();
|
||||
if (numNetworks > -1)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_WIFI_SCANNER_SIZE);
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false);
|
||||
JsonObject root = response.getRoot();
|
||||
JsonArray networks = root.createNestedArray("networks");
|
||||
JsonArray networks = root["networks"].to<JsonArray>();
|
||||
for (int i = 0; i < numNetworks; i++)
|
||||
{
|
||||
JsonObject network = networks.createNestedObject();
|
||||
JsonObject network = networks.add<JsonObject>();
|
||||
network["rssi"] = WiFi.RSSI(i);
|
||||
network["ssid"] = WiFi.SSID(i);
|
||||
network["bssid"] = WiFi.BSSIDstr(i);
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#define SCAN_NETWORKS_SERVICE_PATH "/api/scanNetworks"
|
||||
#define LIST_NETWORKS_SERVICE_PATH "/api/listNetworks"
|
||||
|
||||
#define MAX_WIFI_SCANNER_SIZE 1024
|
||||
|
||||
class WiFiScanner
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -18,9 +18,8 @@ WiFiSettingsService::WiFiSettingsService(PsychicHttpServer *server, FS *fs, Secu
|
||||
EventSocket *socket)
|
||||
: _server(server), _securityManager(securityManager),
|
||||
_httpEndpoint(WiFiSettings::read, WiFiSettings::update, this, server, WIFI_SETTINGS_SERVICE_PATH, securityManager,
|
||||
AuthenticationPredicates::IS_ADMIN, WIFI_SETTINGS_BUFFER_SIZE),
|
||||
_eventEndpoint(WiFiSettings::read, WiFiSettings::update, this, socket, EVENT_WIFI_SETTINGS,
|
||||
WIFI_SETTINGS_BUFFER_SIZE),
|
||||
AuthenticationPredicates::IS_ADMIN),
|
||||
_eventEndpoint(WiFiSettings::read, WiFiSettings::update, this, socket, EVENT_WIFI_SETTINGS),
|
||||
_fsPersistence(WiFiSettings::read, WiFiSettings::update, this, fs, WIFI_SETTINGS_FILE), _lastConnectionAttempt(0),
|
||||
_socket(socket)
|
||||
{
|
||||
|
||||
@@ -50,8 +50,6 @@
|
||||
#define WIFI_RECONNECTION_DELAY 1000 * 30
|
||||
#define RSSI_EVENT_DELAY 500
|
||||
|
||||
#define WIFI_SETTINGS_BUFFER_SIZE 2048
|
||||
|
||||
#define EVENT_RSSI "rssi"
|
||||
#define EVENT_WIFI_SETTINGS "WiFiSettings"
|
||||
|
||||
@@ -83,13 +81,13 @@ public:
|
||||
root["priority_RSSI"] = settings.priorityBySignalStrength;
|
||||
|
||||
// create JSON array from root
|
||||
JsonArray wifiNetworks = root.createNestedArray("wifi_networks");
|
||||
JsonArray wifiNetworks = root["wifi_networks"].to<JsonArray>();
|
||||
|
||||
// iterate over the wifiSettings
|
||||
for (auto &wifi : settings.wifiSettings)
|
||||
{
|
||||
// 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
|
||||
wifiNetwork["ssid"] = wifi.ssid;
|
||||
|
||||
@@ -63,7 +63,7 @@ void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
|
||||
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();
|
||||
wl_status_t status = WiFi.status();
|
||||
root["status"] = (uint8_t)status;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <IPUtils.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define MAX_WIFI_STATUS_SIZE 1024
|
||||
#define WIFI_STATUS_SERVICE_PATH "/api/wifiStatus"
|
||||
|
||||
class WiFiStatus
|
||||
|
||||
@@ -36,20 +36,20 @@ build_unflags = -std=gnu++11
|
||||
test_ignore = test_embedded
|
||||
board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson@^6.21.2
|
||||
ArduinoJson@>=7.0.0
|
||||
https://github.com/theelims/PsychicMqttClient.git#0.1.1
|
||||
; thomasfredericks/Bounce2@ ^2.7.0
|
||||
teckel12/NewPing@^1.9.7
|
||||
adafruit/Adafruit SSD1306@^2.5.7
|
||||
adafruit/Adafruit GFX Library@^1.11.5
|
||||
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 ADS1X15@^2.4.0
|
||||
; adafruit/Adafruit HMC5883 Unified@^1.2.1
|
||||
; adafruit/Adafruit Unified Sensor@^1.1.11
|
||||
; plageoj/UrlEncode@ ^1.0.1
|
||||
rfetick/MPU6050_light@^1.1.0
|
||||
SPI
|
||||
; board_build.partitions = config/no_oat.csv
|
||||
extra_scripts =
|
||||
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