Merge branch 'dev' into dev2

This commit is contained in:
MichaelDvP
2024-03-15 17:43:08 +01:00
52 changed files with 3584 additions and 4061 deletions

6
.gitignore vendored
View File

@@ -1,8 +1,5 @@
# vscode # vscode
.vscode/c_cpp_properties.json .vscode/*
.vscode/extensions.json
.vscode/launch.json
.vscode/settings.json
# c++ compiling # c++ compiling
.clang_complete .clang_complete
@@ -60,3 +57,4 @@ bw-output/
# testing # testing
emsesp emsesp

View File

@@ -2,8 +2,6 @@
// See http://go.microsoft.com/fwlink/?LinkId=827846 // See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format // for the documentation about the extensions.json format
"recommendations": [ "recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"platformio.platformio-ide" "platformio.platformio-ide"
], ],
"unwantedRecommendations": [ "unwantedRecommendations": [

88
.vscode/settings.json vendored
View File

@@ -1,88 +0,0 @@
{
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"eslint.nodePath": "interface/.yarn/sdks",
"eslint.workingDirectories": ["interface"],
"prettier.prettierPath": "",
"typescript.enablePromptUseWorkspaceTsdk": true,
"files.associations": {
"*.tsx": "typescriptreact",
"*.tcc": "cpp",
"optional": "cpp",
"istream": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"system_error": "cpp",
"array": "cpp",
"functional": "cpp",
"regex": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"string": "cpp",
"string_view": "cpp",
"atomic": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"set": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
},
"todo-tree.filtering.excludeGlobs": [
"**/vendor/**",
"**/node_modules/**",
"**/dist/**",
"**/bower_components/**",
"**/build/**",
"**/.vscode/**",
"**/.github/**",
"**/_output/**",
"**/*.min.*",
"**/*.map",
"**/ArduinoJson/**"
],
"cSpell.enableFiletypes": ["!cpp"]
}

18
.vscode/tasks.json vendored
View File

@@ -1,18 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "build standalone emsesp",
"command": "make",
"args": [],
"problemMatcher": ["$gcc"],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@@ -27,6 +27,10 @@
- remote thermostat emulation for RC100H, RC200 and FB10 [#1287](https://github.com/emsesp/EMS-ESP32/discussions/1287), [#1602](https://github.com/emsesp/EMS-ESP32/discussions/1602), [#1551](https://github.com/emsesp/EMS-ESP32/discussions/1551) - remote thermostat emulation for RC100H, RC200 and FB10 [#1287](https://github.com/emsesp/EMS-ESP32/discussions/1287), [#1602](https://github.com/emsesp/EMS-ESP32/discussions/1602), [#1551](https://github.com/emsesp/EMS-ESP32/discussions/1551)
- env and partitions for DevKitC-1-N32R8 [#1635](https://github.com/emsesp/EMS-ESP32/discussions/1635) - env and partitions for DevKitC-1-N32R8 [#1635](https://github.com/emsesp/EMS-ESP32/discussions/1635)
- heatpump dhw stop temperatures [#1624](https://github.com/emsesp/EMS-ESP32/issues/1624) - heatpump dhw stop temperatures [#1624](https://github.com/emsesp/EMS-ESP32/issues/1624)
- weather compensation [#1642](https://github.com/emsesp/EMS-ESP32/issues/1642)
- env and partitions for DevKitC-1-N32R8 [#1635](https://github.com/emsesp/EMS-ESP32/discussions/1635)
- command `restart partitionname` and button long press to start with other partition [#1657](https://github.com/emsesp/EMS-ESP32/issues/1657)
- command `set service <mqtt|ota|ntp|ap> <enable|disable>` [#1663](https://github.com/emsesp/EMS-ESP32/issues/1663)
## Fixed ## Fixed
@@ -42,6 +46,7 @@
- WiFi TxPower wasn't correctly used. Added an 'Auto' setting, which is the default. - WiFi TxPower wasn't correctly used. Added an 'Auto' setting, which is the default.
- MQTT heap check [#622](https://github.com/emsesp/EMS-ESP32/issues/1622) - MQTT heap check [#622](https://github.com/emsesp/EMS-ESP32/issues/1622)
- Slovak language fix [#1636](https://github.com/emsesp/EMS-ESP32/discussions/1636) - Slovak language fix [#1636](https://github.com/emsesp/EMS-ESP32/discussions/1636)
- dns w/wo IPv6 [#1644](https://github.com/emsesp/EMS-ESP32/issues/1644)
## Changed ## Changed
@@ -52,4 +57,5 @@
- Length of mqtt Broker adress [#1619](https://github.com/emsesp/EMS-ESP32/issues/1619) - Length of mqtt Broker adress [#1619](https://github.com/emsesp/EMS-ESP32/issues/1619)
- C++ optimizations - see <https://github.com/emsesp/EMS-ESP32/pull/1615> - C++ optimizations - see <https://github.com/emsesp/EMS-ESP32/pull/1615>
- Send MQTT heartbeat immediately after connection [#1628](https://github.com/emsesp/EMS-ESP32/issues/1628) - Send MQTT heartbeat immediately after connection [#1628](https://github.com/emsesp/EMS-ESP32/issues/1628)
- 16MB partitions with second nvs, larger FS, Coredump - 16MB partitions with second nvs, larger FS, Coredump, optional factory partition
- stop fetching empty telegrams after 5 min

View File

@@ -0,0 +1,9 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x005000,
otadata, data, ota, , 0x002000,
boot, app, factory, , 0x280000,
app0, app, ota_0, , 0x590000,
app1, app, ota_1, , 0x590000,
nvs1, data, nvs, , 0x040000,
spiffs, data, spiffs, , 0x200000,
coredump, data, coredump,, 0x010000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x005000
3 otadata data ota 0x002000
4 boot app factory 0x280000
5 app0 app ota_0 0x590000
6 app1 app ota_1 0x590000
7 nvs1 data nvs 0x040000
8 spiffs data spiffs 0x200000
9 coredump data coredump 0x010000

File diff suppressed because one or more lines are too long

View File

@@ -4,4 +4,4 @@ enableGlobalCache: false
nodeLinker: node-modules nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.1.0.cjs yarnPath: .yarn/releases/yarn-4.1.1.cjs

View File

@@ -26,14 +26,14 @@
"@babel/core": "^7.24.0", "@babel/core": "^7.24.0",
"@emotion/react": "^11.11.4", "@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.12", "@mui/icons-material": "^5.15.13",
"@mui/material": "^5.15.12", "@mui/material": "^5.15.13",
"@table-library/react-table-library": "4.1.7", "@table-library/react-table-library": "4.1.7",
"@types/imagemin": "^8.0.5", "@types/imagemin": "^8.0.5",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.25", "@types/node": "^20.11.28",
"@types/react": "^18.2.64", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.21", "@types/react-dom": "^18.2.22",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"alova": "^2.17.1", "alova": "^2.17.1",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
@@ -45,17 +45,17 @@
"react-dom": "latest", "react-dom": "latest",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-icons": "^5.0.1", "react-icons": "^5.0.1",
"react-router-dom": "^6.22.2", "react-router-dom": "^6.22.3",
"react-toastify": "^10.0.4", "react-toastify": "^10.0.5",
"sockette": "^2.0.6", "sockette": "^2.0.6",
"typesafe-i18n": "^5.26.2", "typesafe-i18n": "^5.26.2",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"devDependencies": { "devDependencies": {
"@preact/compat": "^17.1.2", "@preact/compat": "^17.1.2",
"@preact/preset-vite": "^2.8.1", "@preact/preset-vite": "^2.8.2",
"@typescript-eslint/eslint-plugin": "^7.1.1", "@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.1.1", "@typescript-eslint/parser": "^7.2.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
@@ -69,10 +69,10 @@
"preact": "^10.19.6", "preact": "^10.19.6",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"terser": "^5.29.1", "terser": "^5.29.2",
"vite": "^5.1.5", "vite": "^5.1.6",
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^4.3.1" "vite-tsconfig-paths": "^4.3.2"
}, },
"packageManager": "yarn@4.0.2" "packageManager": "yarn@4.1.1"
} }

View File

@@ -12,7 +12,7 @@ const bytesPerLine = 20;
var totalSize = 0; var totalSize = 0;
const generateWWWClass = () => const generateWWWClass = () =>
`typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len, const String& hash)> RouteRegistrationHandler; `typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler;
// Total size is ${totalSize} bytes // Total size is ${totalSize} bytes
class WWWData { class WWWData {

File diff suppressed because it is too large Load Diff

View File

@@ -63,7 +63,7 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
~AsyncJsonResponse() { ~AsyncJsonResponse() {
} }
JsonVariant & getRoot() { JsonVariant getRoot() {
return _root; return _root;
} }
bool _sourceValid() const { bool _sourceValid() const {
@@ -108,7 +108,7 @@ class PrettyAsyncJsonResponse : public AsyncJsonResponse {
} }
}; };
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant & json)> ArJsonRequestHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request, JsonVariant json)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler : public AsyncWebHandler { class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
private: private:
@@ -142,13 +142,15 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
if (!_onRequest) if (!_onRequest)
return false; return false;
if (!(_method & request->method())) WebRequestMethodComposite request_method = request->method();
if (!(_method & request_method))
return false; return false;
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
return false; return false;
if (!request->contentType().equalsIgnoreCase(JSON_MIMETYPE)) if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(JSON_MIMETYPE))
return false; return false;
request->addInterestingHeader("ANY"); request->addInterestingHeader("ANY");
@@ -156,18 +158,23 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
} }
virtual void handleRequest(AsyncWebServerRequest * request) override final { virtual void handleRequest(AsyncWebServerRequest * request) override final {
if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if (_onRequest) { if (_onRequest) {
JsonVariant json; // empty variant if (request->method() == HTTP_GET) {
if (request->_tempObject != NULL) { JsonVariant json;
_onRequest(request, json);
return;
} else if (request->_tempObject != NULL) {
JsonDocument jsonBuffer; JsonDocument jsonBuffer;
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject));
if (!error) { if (!error) {
json = jsonBuffer.as<JsonVariant>(); JsonVariant json = jsonBuffer.as<JsonVariant>();
_onRequest(request, json); _onRequest(request, json);
return; return;
} }
} }
_onRequest(request, json); request->send(_contentLength > _maxContentLength ? 413 : 400);
} else { } else {
request->send(500); request->send(500);
} }

View File

@@ -28,6 +28,8 @@
#include <vector> #include <vector>
#include "FS.h" #include "FS.h"
#include <ArduinoJson.h> // added by proddy
#include "StringArray.h" #include "StringArray.h"
#ifdef ESP32 #ifdef ESP32
@@ -78,13 +80,13 @@ typedef enum {
#ifndef HAVE_FS_FILE_OPEN_MODE #ifndef HAVE_FS_FILE_OPEN_MODE
namespace fs { namespace fs {
class FileOpenMode { class FileOpenMode {
public: public:
static const char *read; static const char * read;
static const char *write; static const char * write;
static const char *append; static const char * append;
};
}; };
}; // namespace fs
#else #else
#include "FileOpenMode.h" #include "FileOpenMode.h"
#endif #endif
@@ -108,13 +110,28 @@ class AsyncWebParameter {
bool _isFile; bool _isFile;
public: public:
AsyncWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0)
AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){} : _name(name)
const String& name() const { return _name; } , _value(value)
const String& value() const { return _value; } , _size(size)
size_t size() const { return _size; } , _isForm(form)
bool isPost() const { return _isForm; } , _isFile(file) {
bool isFile() const { return _isFile; } }
const String & name() const {
return _name;
}
const String & value() const {
return _value;
}
size_t size() const {
return _size;
}
bool isPost() const {
return _isForm;
}
bool isFile() const {
return _isFile;
}
}; };
/* /*
@@ -130,20 +147,33 @@ class AsyncWebHeader {
AsyncWebHeader() = default; AsyncWebHeader() = default;
AsyncWebHeader(const AsyncWebHeader &) = default; AsyncWebHeader(const AsyncWebHeader &) = default;
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){} AsyncWebHeader(const String & name, const String & value)
AsyncWebHeader(const String& data): _name(), _value(){ : _name(name)
if(!data) return; , _value(value) {
}
AsyncWebHeader(const String & data)
: _name()
, _value() {
if (!data)
return;
int index = data.indexOf(':'); int index = data.indexOf(':');
if (index < 0) return; if (index < 0)
return;
_name = data.substring(0, index); _name = data.substring(0, index);
_value = data.substring(index + 2); _value = data.substring(index + 2);
} }
AsyncWebHeader &operator=(const AsyncWebHeader &) = default; AsyncWebHeader & operator=(const AsyncWebHeader &) = default;
const String& name() const { return _name; } const String & name() const {
const String& value() const { return _value; } return _name;
String toString() const { return _name + F(": ") + _value + F("\r\n"); } }
const String & value() const {
return _value;
}
String toString() const {
return _name + F(": ") + _value + F("\r\n");
}
}; };
/* /*
@@ -152,19 +182,20 @@ class AsyncWebHeader {
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller; typedef std::function<size_t(uint8_t *, size_t, size_t)> AwsResponseFiller;
typedef std::function<String(const String&)> AwsTemplateProcessor; typedef std::function<String(const String &)> AwsTemplateProcessor;
class AsyncWebServerRequest { class AsyncWebServerRequest {
using File = fs::File; using File = fs::File;
using FS = fs::FS; using FS = fs::FS;
friend class AsyncWebServer; friend class AsyncWebServer;
friend class AsyncCallbackWebHandler; friend class AsyncCallbackWebHandler;
private: private:
AsyncClient* _client; AsyncClient * _client;
AsyncWebServer* _server; AsyncWebServer * _server;
AsyncWebHandler* _handler; AsyncWebHandler * _handler;
AsyncWebServerResponse* _response; AsyncWebServerResponse * _response;
std::vector<String> _interestingHeaders; std::vector<String> _interestingHeaders;
ArDisconnectHandler _onDisconnectfn; ArDisconnectHandler _onDisconnectfn;
@@ -199,7 +230,7 @@ class AsyncWebServerRequest {
String _itemFilename; String _itemFilename;
String _itemType; String _itemType;
String _itemValue; String _itemValue;
uint8_t *_itemBuffer; uint8_t * _itemBuffer;
size_t _itemBufferIndex; size_t _itemBufferIndex;
bool _itemIsFile; bool _itemIsFile;
@@ -208,17 +239,17 @@ class AsyncWebServerRequest {
void _onError(int8_t error); void _onError(int8_t error);
void _onTimeout(uint32_t time); void _onTimeout(uint32_t time);
void _onDisconnect(); void _onDisconnect();
void _onData(void *buf, size_t len); void _onData(void * buf, size_t len);
void _addParam(AsyncWebParameter*); void _addParam(AsyncWebParameter *);
void _addPathParam(const char *param); void _addPathParam(const char * param);
bool _parseReqHead(); bool _parseReqHead();
bool _parseReqHeader(); bool _parseReqHeader();
void _parseLine(); void _parseLine();
void _parsePlainPostChar(uint8_t data); void _parsePlainPostChar(uint8_t data);
void _parseMultipartPostByte(uint8_t data, bool last); void _parseMultipartPostByte(uint8_t data, bool last);
void _addGetParams(const String& params); void _addGetParams(const String & params);
void _handleUploadStart(); void _handleUploadStart();
void _handleUploadByte(uint8_t data, bool last); void _handleUploadByte(uint8_t data, bool last);
@@ -226,24 +257,42 @@ class AsyncWebServerRequest {
public: public:
File _tempFile; File _tempFile;
void *_tempObject; void * _tempObject;
AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
~AsyncWebServerRequest(); ~AsyncWebServerRequest();
AsyncClient* client(){ return _client; } AsyncClient * client() {
uint8_t version() const { return _version; } return _client;
WebRequestMethodComposite method() const { return _method; } }
const String& url() const { return _url; } uint8_t version() const {
const String& host() const { return _host; } return _version;
const String& contentType() const { return _contentType; } }
size_t contentLength() const { return _contentLength; } WebRequestMethodComposite method() const {
bool multipart() const { return _isMultipart; } return _method;
const __FlashStringHelper *methodToString() const; }
const __FlashStringHelper *requestedConnTypeToString() const; const String & url() const {
RequestedConnectionType requestedConnType() const { return _reqconntype; } return _url;
}
const String & host() const {
return _host;
}
const String & contentType() const {
return _contentType;
}
size_t contentLength() const {
return _contentLength;
}
bool multipart() const {
return _isMultipart;
}
const __FlashStringHelper * methodToString() const;
const __FlashStringHelper * requestedConnTypeToString() const;
RequestedConnectionType requestedConnType() const {
return _reqconntype;
}
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
void onDisconnect (ArDisconnectHandler fn); void onDisconnect(ArDisconnectHandler fn);
//hash is the string representation of: //hash is the string representation of:
// base64(user:pass) for basic or // base64(user:pass) for basic or
@@ -252,76 +301,85 @@ class AsyncWebServerRequest {
bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false); bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
void requestAuthentication(const char * realm = NULL, bool isDigest = true); void requestAuthentication(const char * realm = NULL, bool isDigest = true);
void setHandler(AsyncWebHandler *handler){ _handler = handler; } void setHandler(AsyncWebHandler * handler) {
void addInterestingHeader(const String& name); _handler = handler;
}
void addInterestingHeader(const String & name);
void redirect(const String& url); void redirect(const String & url);
void send(AsyncWebServerResponse *response); void send(AsyncWebServerResponse * response);
void send(int code, const String& contentType=String(), const String& content=String()); void send(int code, const String & contentType = String(), const String & content = String());
void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); void send(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); void send(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); void send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr);
void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); void send(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); void sendChunked(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); void send_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr);
void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); void send_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String()); AsyncWebServerResponse * beginResponse(int code, const String & contentType = String(), const String & content = String());
AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); AsyncWebServerResponse *
AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); beginResponse(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); AsyncWebServerResponse *
AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); beginResponse(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); AsyncWebServerResponse * beginResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460); AsyncWebServerResponse * beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); AsyncWebServerResponse * beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); AsyncResponseStream * beginResponseStream(const String & contentType, size_t bufferSize = 1460);
AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
// added by proddy
AsyncWebServerResponse * beginResponse(const String & contentType, const uint8_t * content, size_t len);
size_t headers() const; // get header count size_t headers() const; // get header count
bool hasHeader(const String& name) const; // check if header exists bool hasHeader(const String & name) const; // check if header exists
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
AsyncWebHeader* getHeader(const String& name); AsyncWebHeader * getHeader(const String & name);
const AsyncWebHeader* getHeader(const String& name) const; const AsyncWebHeader * getHeader(const String & name) const;
AsyncWebHeader* getHeader(const __FlashStringHelper * data); AsyncWebHeader * getHeader(const __FlashStringHelper * data);
const AsyncWebHeader* getHeader(const __FlashStringHelper * data) const; const AsyncWebHeader * getHeader(const __FlashStringHelper * data) const;
AsyncWebHeader* getHeader(size_t num); AsyncWebHeader * getHeader(size_t num);
const AsyncWebHeader* getHeader(size_t num) const; const AsyncWebHeader * getHeader(size_t num) const;
size_t params() const; // get arguments count size_t params() const; // get arguments count
bool hasParam(const String& name, bool post=false, bool file=false) const; bool hasParam(const String & name, bool post = false, bool file = false) const;
bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const; bool hasParam(const __FlashStringHelper * data, bool post = false, bool file = false) const;
AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const; AsyncWebParameter * getParam(const String & name, bool post = false, bool file = false) const;
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; AsyncWebParameter * getParam(const __FlashStringHelper * data, bool post, bool file) const;
AsyncWebParameter* getParam(size_t num) const; AsyncWebParameter * getParam(size_t num) const;
size_t args() const { return params(); } // get arguments count size_t args() const {
const String& arg(const String& name) const; // get request argument value by name return params();
const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name) } // get arguments count
const String& arg(size_t i) const; // get request argument value by number const String & arg(const String & name) const; // get request argument value by name
const String& argName(size_t i) const; // get request argument name by number const String & arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
bool hasArg(const char* name) const; // check if argument exists const String & arg(size_t i) const; // get request argument value by number
const String & argName(size_t i) const; // get request argument name by number
bool hasArg(const char * name) const; // check if argument exists
bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; const String & ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
const String& header(const char* name) const;// get request header value by name const String & header(const char * name) const; // get request header value by name
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name) const String & header(const __FlashStringHelper * data) const; // get request header value by F(name)
const String& header(size_t i) const; // get request header value by number const String & header(size_t i) const; // get request header value by number
const String& headerName(size_t i) const; // get request header name by number const String & headerName(size_t i) const; // get request header name by number
String urlDecode(const String& text) const; String urlDecode(const String & text) const;
}; };
/* /*
* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
* */ * */
typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction; typedef std::function<bool(AsyncWebServerRequest * request)> ArRequestFilterFunction;
bool ON_STA_FILTER(AsyncWebServerRequest *request); bool ON_STA_FILTER(AsyncWebServerRequest * request);
bool ON_AP_FILTER(AsyncWebServerRequest *request); bool ON_AP_FILTER(AsyncWebServerRequest * request);
/* /*
* REWRITE :: One instance can be handle any Request (done by the Server) * REWRITE :: One instance can be handle any Request (done by the Server)
@@ -333,21 +391,40 @@ class AsyncWebRewrite {
String _toUrl; String _toUrl;
String _params; String _params;
ArRequestFilterFunction _filter; ArRequestFilterFunction _filter;
public: public:
AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){ AsyncWebRewrite(const char * from, const char * to)
: _from(from)
, _toUrl(to)
, _params(String())
, _filter(NULL) {
int index = _toUrl.indexOf('?'); int index = _toUrl.indexOf('?');
if (index > 0) { if (index > 0) {
_params = _toUrl.substring(index +1); _params = _toUrl.substring(index + 1);
_toUrl = _toUrl.substring(0, index); _toUrl = _toUrl.substring(0, index);
} }
} }
virtual ~AsyncWebRewrite(){} virtual ~AsyncWebRewrite() {
AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } }
bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); } AsyncWebRewrite & setFilter(ArRequestFilterFunction fn) {
const String& from(void) const { return _from; } _filter = fn;
const String& toUrl(void) const { return _toUrl; } return *this;
const String& params(void) const { return _params; } }
virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); } bool filter(AsyncWebServerRequest * request) const {
return _filter == NULL || _filter(request);
}
const String & from(void) const {
return _from;
}
const String & toUrl(void) const {
return _toUrl;
}
const String & params(void) const {
return _params;
}
virtual bool match(AsyncWebServerRequest * request) {
return from() == request->url() && filter(request);
}
}; };
/* /*
@@ -359,28 +436,54 @@ class AsyncWebHandler {
ArRequestFilterFunction _filter; ArRequestFilterFunction _filter;
String _username; String _username;
String _password; String _password;
public: public:
AsyncWebHandler():_username(""), _password(""){} AsyncWebHandler()
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } : _username("")
AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; }; , _password("") {
bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); } }
virtual ~AsyncWebHandler(){} AsyncWebHandler & setFilter(ArRequestFilterFunction fn) {
virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){ _filter = fn;
return *this;
}
AsyncWebHandler & setAuthentication(const char * username, const char * password) {
_username = String(username);
_password = String(password);
return *this;
};
bool filter(AsyncWebServerRequest * request) {
return _filter == NULL || _filter(request);
}
virtual ~AsyncWebHandler() {
}
virtual bool canHandle(AsyncWebServerRequest * request __attribute__((unused))) {
return false; return false;
} }
virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){} virtual void handleRequest(AsyncWebServerRequest * request __attribute__((unused))) {
virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){} }
virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){} virtual void handleUpload(AsyncWebServerRequest * request __attribute__((unused)),
virtual bool isRequestHandlerTrivial(){return true;} const String & filename __attribute__((unused)),
size_t index __attribute__((unused)),
uint8_t * data __attribute__((unused)),
size_t len __attribute__((unused)),
bool final __attribute__((unused))) {
}
virtual void handleBody(AsyncWebServerRequest * request __attribute__((unused)),
uint8_t * data __attribute__((unused)),
size_t len __attribute__((unused)),
size_t index __attribute__((unused)),
size_t total __attribute__((unused))) {
}
virtual bool isRequestHandlerTrivial() {
return true;
}
}; };
/* /*
* RESPONSE :: One instance is created for each Request (attached by the Handler) * RESPONSE :: One instance is created for each Request (attached by the Handler)
* */ * */
typedef enum { typedef enum { RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState;
RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
} WebResponseState;
class AsyncWebServerResponse { class AsyncWebServerResponse {
protected: protected:
@@ -395,40 +498,43 @@ class AsyncWebServerResponse {
size_t _ackedLength; size_t _ackedLength;
size_t _writtenLength; size_t _writtenLength;
WebResponseState _state; WebResponseState _state;
const char* _responseCodeToString(int code); const char * _responseCodeToString(int code);
public:
static const __FlashStringHelper *responseCodeToString(int code); public:
static const __FlashStringHelper * responseCodeToString(int code);
public: public:
AsyncWebServerResponse(); AsyncWebServerResponse();
virtual ~AsyncWebServerResponse(); virtual ~AsyncWebServerResponse();
virtual void setCode(int code); virtual void setCode(int code);
virtual void setContentLength(size_t len); virtual void setContentLength(size_t len);
virtual void setContentType(const String& type); virtual void setContentType(const String & type);
virtual void addHeader(const String& name, const String& value); virtual void addHeader(const String & name, const String & value);
virtual String _assembleHead(uint8_t version); virtual String _assembleHead(uint8_t version);
virtual bool _started() const; virtual bool _started() const;
virtual bool _finished() const; virtual bool _finished() const;
virtual bool _failed() const; virtual bool _failed() const;
virtual bool _sourceValid() const; virtual bool _sourceValid() const;
virtual void _respond(AsyncWebServerRequest *request); virtual void _respond(AsyncWebServerRequest * request);
virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); virtual size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time);
}; };
/* /*
* SERVER :: One instance * SERVER :: One instance
* */ * */
typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request)> ArRequestHandlerFunction;
typedef std::function<void(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final)> ArUploadHandlerFunction;
typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant json)> ArJsonRequestHandlerFunction; // added by proddy
class AsyncWebServer { class AsyncWebServer {
protected: protected:
AsyncServer _server; AsyncServer _server;
LinkedList<AsyncWebRewrite*> _rewrites; LinkedList<AsyncWebRewrite *> _rewrites;
LinkedList<AsyncWebHandler*> _handlers; LinkedList<AsyncWebHandler *> _handlers;
AsyncCallbackWebHandler* _catchAllHandler; AsyncCallbackWebHandler * _catchAllHandler;
public: public:
AsyncWebServer(uint16_t port); AsyncWebServer(uint16_t port);
@@ -438,23 +544,26 @@ class AsyncWebServer {
void end(); void end();
#if ASYNC_TCP_SSL_ENABLED #if ASYNC_TCP_SSL_ENABLED
void onSslFileRequest(AcSSlFileHandler cb, void* arg); void onSslFileRequest(AcSSlFileHandler cb, void * arg);
void beginSecure(const char *cert, const char *private_key_file, const char *password); void beginSecure(const char * cert, const char * private_key_file, const char * password);
#endif #endif
AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite); AsyncWebRewrite & addRewrite(AsyncWebRewrite * rewrite);
bool removeRewrite(AsyncWebRewrite* rewrite); bool removeRewrite(AsyncWebRewrite * rewrite);
AsyncWebRewrite& rewrite(const char* from, const char* to); AsyncWebRewrite & rewrite(const char * from, const char * to);
AsyncWebHandler& addHandler(AsyncWebHandler* handler); AsyncWebHandler & addHandler(AsyncWebHandler * handler);
bool removeHandler(AsyncWebHandler* handler); bool removeHandler(AsyncWebHandler * handler);
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest); AsyncCallbackWebHandler & on(const char * uri, ArRequestHandlerFunction onRequest);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest); AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); AsyncCallbackWebHandler &
on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL); void on(const char * uri, ArJsonRequestHandlerFunction onRequest); // added by proddy
AsyncStaticWebHandler & serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control = NULL);
void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
@@ -462,31 +571,35 @@ class AsyncWebServer {
void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
void _handleDisconnect(AsyncWebServerRequest *request); void _handleDisconnect(AsyncWebServerRequest * request);
void _attachHandler(AsyncWebServerRequest *request); void _attachHandler(AsyncWebServerRequest * request);
void _rewriteRequest(AsyncWebServerRequest *request); void _rewriteRequest(AsyncWebServerRequest * request);
}; };
class DefaultHeaders { class DefaultHeaders {
using headers_t = std::list<AsyncWebHeader>; using headers_t = std::list<AsyncWebHeader>;
headers_t _headers; headers_t _headers;
public: public:
DefaultHeaders() = default; DefaultHeaders() = default;
using ConstIterator = headers_t::const_iterator; using ConstIterator = headers_t::const_iterator;
void addHeader(const String& name, const String& value){ void addHeader(const String & name, const String & value) {
_headers.emplace_back(name, value); _headers.emplace_back(name, value);
} }
ConstIterator begin() const { return _headers.begin(); } ConstIterator begin() const {
ConstIterator end() const { return _headers.end(); } return _headers.begin();
}
ConstIterator end() const {
return _headers.end();
}
DefaultHeaders(DefaultHeaders const &) = delete; DefaultHeaders(DefaultHeaders const &) = delete;
DefaultHeaders &operator=(DefaultHeaders const &) = delete; DefaultHeaders & operator=(DefaultHeaders const &) = delete;
static DefaultHeaders &Instance() { static DefaultHeaders & Instance() {
static DefaultHeaders instance; static DefaultHeaders instance;
return instance; return instance;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -31,17 +31,20 @@
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
class AsyncBasicResponse: public AsyncWebServerResponse { class AsyncBasicResponse : public AsyncWebServerResponse {
private: private:
String _content; String _content;
public: public:
AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String()); AsyncBasicResponse(int code, const String & contentType = String(), const String & content = String());
void _respond(AsyncWebServerRequest *request); void _respond(AsyncWebServerRequest * request);
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time);
bool _sourceValid() const { return true; } bool _sourceValid() const {
return true;
}
}; };
class AsyncAbstractResponse: public AsyncWebServerResponse { class AsyncAbstractResponse : public AsyncWebServerResponse {
private: private:
String _head; String _head;
// Data is inserted into cache at begin(). // Data is inserted into cache at begin().
@@ -49,16 +52,22 @@ class AsyncAbstractResponse: public AsyncWebServerResponse {
// we won't be able to access it as contiguous array of bytes when reading from it, // we won't be able to access it as contiguous array of bytes when reading from it,
// so by gaining performance in one place, we'll lose it in another. // so by gaining performance in one place, we'll lose it in another.
std::vector<uint8_t> _cache; std::vector<uint8_t> _cache;
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _readDataFromCacheOrContent(uint8_t * data, const size_t len);
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); size_t _fillBufferAndProcessTemplates(uint8_t * buf, size_t maxLen);
protected: protected:
AwsTemplateProcessor _callback; AwsTemplateProcessor _callback;
public: public:
AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr); AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr);
void _respond(AsyncWebServerRequest *request); void _respond(AsyncWebServerRequest * request);
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time);
bool _sourceValid() const { return false; } bool _sourceValid() const {
virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } return false;
}
virtual size_t _fillBuffer(uint8_t * buf __attribute__((unused)), size_t maxLen __attribute__((unused))) {
return 0;
}
}; };
#ifndef TEMPLATE_PLACEHOLDER #ifndef TEMPLATE_PLACEHOLDER
@@ -66,71 +75,90 @@ class AsyncAbstractResponse: public AsyncWebServerResponse {
#endif #endif
#define TEMPLATE_PARAM_NAME_LENGTH 32 #define TEMPLATE_PARAM_NAME_LENGTH 32
class AsyncFileResponse: public AsyncAbstractResponse { class AsyncFileResponse : public AsyncAbstractResponse {
using File = fs::File; using File = fs::File;
using FS = fs::FS; using FS = fs::FS;
private: private:
File _content; File _content;
String _path; String _path;
void _setContentType(const String& path); void _setContentType(const String & path);
public: public:
AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); AsyncFileResponse(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); AsyncFileResponse(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
~AsyncFileResponse(); ~AsyncFileResponse();
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const {
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; return !!(_content);
}
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
}; };
class AsyncStreamResponse: public AsyncAbstractResponse { class AsyncStreamResponse : public AsyncAbstractResponse {
private: private:
Stream *_content; Stream * _content;
public: public:
AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr);
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const {
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; return !!(_content);
}
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
}; };
class AsyncCallbackResponse: public AsyncAbstractResponse { class AsyncCallbackResponse : public AsyncAbstractResponse {
private: private:
AwsResponseFiller _content; AwsResponseFiller _content;
size_t _filledLength; size_t _filledLength;
public: public:
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const {
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; return !!(_content);
}
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
}; };
class AsyncChunkedResponse: public AsyncAbstractResponse { class AsyncChunkedResponse : public AsyncAbstractResponse {
private: private:
AwsResponseFiller _content; AwsResponseFiller _content;
size_t _filledLength; size_t _filledLength;
public: public:
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const {
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; return !!(_content);
}
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
}; };
class AsyncProgmemResponse: public AsyncAbstractResponse { class AsyncProgmemResponse : public AsyncAbstractResponse {
private: private:
const uint8_t * _content; const uint8_t * _content;
size_t _readLength; size_t _readLength;
public: public:
AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr);
bool _sourceValid() const { return true; } bool _sourceValid() const {
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; return true;
}
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
}; };
class cbuf; class cbuf;
class AsyncResponseStream: public AsyncAbstractResponse, public Print { class AsyncResponseStream : public AsyncAbstractResponse, public Print {
private: private:
std::unique_ptr<cbuf> _content; std::unique_ptr<cbuf> _content;
public: public:
AsyncResponseStream(const String& contentType, size_t bufferSize); AsyncResponseStream(const String & contentType, size_t bufferSize);
~AsyncResponseStream(); ~AsyncResponseStream();
bool _sourceValid() const { return (_state < RESPONSE_END); } bool _sourceValid() const {
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; return (_state < RESPONSE_END);
size_t write(const uint8_t *data, size_t len); }
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
size_t write(const uint8_t * data, size_t len);
size_t write(uint8_t data); size_t write(uint8_t data);
using Print::write; using Print::write;
}; };

View File

@@ -23,11 +23,10 @@
#include "cbuf.h" #include "cbuf.h"
// Since ESP8266 does not link memchr by default, here's its implementation. // Since ESP8266 does not link memchr by default, here's its implementation.
void* memchr(void* ptr, int ch, size_t count) void * memchr(void * ptr, int ch, size_t count) {
{ unsigned char * p = static_cast<unsigned char *>(ptr);
unsigned char* p = static_cast<unsigned char*>(ptr); while (count--)
while(count--) if (*p++ == static_cast<unsigned char>(ch))
if(*p++ == static_cast<unsigned char>(ch))
return --p; return --p;
return nullptr; return nullptr;
} }
@@ -36,53 +35,94 @@ void* memchr(void* ptr, int ch, size_t count)
/* /*
* Abstract Response * Abstract Response
* */ * */
const char* AsyncWebServerResponse::_responseCodeToString(int code) { const char * AsyncWebServerResponse::_responseCodeToString(int code) {
return reinterpret_cast<const char *>(responseCodeToString(code)); return reinterpret_cast<const char *>(responseCodeToString(code));
} }
const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code) { const __FlashStringHelper * AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) { switch (code) {
case 100: return F("Continue"); case 100:
case 101: return F("Switching Protocols"); return F("Continue");
case 200: return F("OK"); case 101:
case 201: return F("Created"); return F("Switching Protocols");
case 202: return F("Accepted"); case 200:
case 203: return F("Non-Authoritative Information"); return F("OK");
case 204: return F("No Content"); case 201:
case 205: return F("Reset Content"); return F("Created");
case 206: return F("Partial Content"); case 202:
case 300: return F("Multiple Choices"); return F("Accepted");
case 301: return F("Moved Permanently"); case 203:
case 302: return F("Found"); return F("Non-Authoritative Information");
case 303: return F("See Other"); case 204:
case 304: return F("Not Modified"); return F("No Content");
case 305: return F("Use Proxy"); case 205:
case 307: return F("Temporary Redirect"); return F("Reset Content");
case 400: return F("Bad Request"); case 206:
case 401: return F("Unauthorized"); return F("Partial Content");
case 402: return F("Payment Required"); case 300:
case 403: return F("Forbidden"); return F("Multiple Choices");
case 404: return F("Not Found"); case 301:
case 405: return F("Method Not Allowed"); return F("Moved Permanently");
case 406: return F("Not Acceptable"); case 302:
case 407: return F("Proxy Authentication Required"); return F("Found");
case 408: return F("Request Time-out"); case 303:
case 409: return F("Conflict"); return F("See Other");
case 410: return F("Gone"); case 304:
case 411: return F("Length Required"); return F("Not Modified");
case 412: return F("Precondition Failed"); case 305:
case 413: return F("Request Entity Too Large"); return F("Use Proxy");
case 414: return F("Request-URI Too Large"); case 307:
case 415: return F("Unsupported Media Type"); return F("Temporary Redirect");
case 416: return F("Requested range not satisfiable"); case 400:
case 417: return F("Expectation Failed"); return F("Bad Request");
case 500: return F("Internal Server Error"); case 401:
case 501: return F("Not Implemented"); return F("Unauthorized");
case 502: return F("Bad Gateway"); case 402:
case 503: return F("Service Unavailable"); return F("Payment Required");
case 504: return F("Gateway Time-out"); case 403:
case 505: return F("HTTP Version not supported"); return F("Forbidden");
default: return F(""); case 404:
return F("Not Found");
case 405:
return F("Method Not Allowed");
case 406:
return F("Not Acceptable");
case 407:
return F("Proxy Authentication Required");
case 408:
return F("Request Time-out");
case 409:
return F("Conflict");
case 410:
return F("Gone");
case 411:
return F("Length Required");
case 412:
return F("Precondition Failed");
case 413:
return F("Request Entity Too Large");
case 414:
return F("Request-URI Too Large");
case 415:
return F("Unsupported Media Type");
case 416:
return F("Requested range not satisfiable");
case 417:
return F("Expectation Failed");
case 500:
return F("Internal Server Error");
case 501:
return F("Not Implemented");
case 502:
return F("Bad Gateway");
case 503:
return F("Service Unavailable");
case 504:
return F("Gateway Time-out");
case 505:
return F("HTTP Version not supported");
default:
return F("");
} }
} }
@@ -96,38 +136,37 @@ AsyncWebServerResponse::AsyncWebServerResponse()
, _sentLength(0) , _sentLength(0)
, _ackedLength(0) , _ackedLength(0)
, _writtenLength(0) , _writtenLength(0)
, _state(RESPONSE_SETUP) , _state(RESPONSE_SETUP) {
{ for (const auto & header : DefaultHeaders::Instance()) {
for(const auto &header: DefaultHeaders::Instance()) {
_headers.emplace_back(header); _headers.emplace_back(header);
} }
} }
AsyncWebServerResponse::~AsyncWebServerResponse() = default; AsyncWebServerResponse::~AsyncWebServerResponse() = default;
void AsyncWebServerResponse::setCode(int code){ void AsyncWebServerResponse::setCode(int code) {
if(_state == RESPONSE_SETUP) if (_state == RESPONSE_SETUP)
_code = code; _code = code;
} }
void AsyncWebServerResponse::setContentLength(size_t len){ void AsyncWebServerResponse::setContentLength(size_t len) {
if(_state == RESPONSE_SETUP) if (_state == RESPONSE_SETUP)
_contentLength = len; _contentLength = len;
} }
void AsyncWebServerResponse::setContentType(const String& type){ void AsyncWebServerResponse::setContentType(const String & type) {
if(_state == RESPONSE_SETUP) if (_state == RESPONSE_SETUP)
_contentType = type; _contentType = type;
} }
void AsyncWebServerResponse::addHeader(const String& name, const String& value){ void AsyncWebServerResponse::addHeader(const String & name, const String & value) {
_headers.emplace_back(name, value); _headers.emplace_back(name, value);
} }
String AsyncWebServerResponse::_assembleHead(uint8_t version){ String AsyncWebServerResponse::_assembleHead(uint8_t version) {
if(version){ if (version) {
addHeader(F("Accept-Ranges"), F("none")); addHeader(F("Accept-Ranges"), F("none"));
if(_chunked) if (_chunked)
addHeader(F("Transfer-Encoding"), F("chunked")); addHeader(F("Transfer-Encoding"), F("chunked"));
} }
String out = String(); String out = String();
@@ -137,16 +176,16 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code)); snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
out.concat(buf); out.concat(buf);
if(_sendContentLength) { if (_sendContentLength) {
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength); snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
out.concat(buf); out.concat(buf);
} }
if(_contentType.length()) { if (_contentType.length()) {
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str()); snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
out.concat(buf); out.concat(buf);
} }
for(const auto& header: _headers){ for (const auto & header : _headers) {
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str()); snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
out.concat(buf); out.concat(buf);
} }
@@ -157,48 +196,64 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){
return out; return out;
} }
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; } bool AsyncWebServerResponse::_started() const {
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } return _state > RESPONSE_SETUP;
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } }
bool AsyncWebServerResponse::_sourceValid() const { return false; } bool AsyncWebServerResponse::_finished() const {
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); } return _state > RESPONSE_WAIT_ACK;
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; } }
bool AsyncWebServerResponse::_failed() const {
return _state == RESPONSE_FAILED;
}
bool AsyncWebServerResponse::_sourceValid() const {
return false;
}
void AsyncWebServerResponse::_respond(AsyncWebServerRequest * request) {
_state = RESPONSE_END;
request->client()->close();
}
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) {
(void)request;
(void)len;
(void)time;
return 0;
}
/* /*
* String/Code Response * String/Code Response
* */ * */
AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){ AsyncBasicResponse::AsyncBasicResponse(int code, const String & contentType, const String & content) {
_code = code; _code = code;
_content = content; _content = content;
_contentType = contentType; _contentType = contentType;
if(_content.length()){ if (_content.length()) {
_contentLength = _content.length(); _contentLength = _content.length();
if(!_contentType.length()) if (!_contentType.length())
_contentType = F("text/plain"); _contentType = F("text/plain");
} }
addHeader(F("Connection"), F("close")); addHeader(F("Connection"), F("close"));
} }
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ void AsyncBasicResponse::_respond(AsyncWebServerRequest * request) {
_state = RESPONSE_HEADERS; _state = RESPONSE_HEADERS;
String out = _assembleHead(request->version()); String out = _assembleHead(request->version());
size_t outLen = out.length(); size_t outLen = out.length();
size_t space = request->client()->space(); size_t space = request->client()->space();
if(!_contentLength && space >= outLen){ if (!_contentLength && space >= outLen) {
_writtenLength += request->client()->write(out.c_str(), outLen); _writtenLength += request->client()->write(out.c_str(), outLen);
_state = RESPONSE_WAIT_ACK; _state = RESPONSE_WAIT_ACK;
} else if(_contentLength && space >= outLen + _contentLength){ } else if (_contentLength && space >= outLen + _contentLength) {
out += _content; out += _content;
outLen += _contentLength; outLen += _contentLength;
_writtenLength += request->client()->write(out.c_str(), outLen); _writtenLength += request->client()->write(out.c_str(), outLen);
_state = RESPONSE_WAIT_ACK; _state = RESPONSE_WAIT_ACK;
} else if(space && space < outLen){ } else if (space && space < outLen) {
String partial = out.substring(0, space); String partial = out.substring(0, space);
_content = out.substring(space) + _content; _content = out.substring(space) + _content;
_contentLength += outLen - space; _contentLength += outLen - space;
_writtenLength += request->client()->write(partial.c_str(), partial.length()); _writtenLength += request->client()->write(partial.c_str(), partial.length());
_state = RESPONSE_CONTENT; _state = RESPONSE_CONTENT;
} else if(space > outLen && space < (outLen + _contentLength)){ } else if (space > outLen && space < (outLen + _contentLength)) {
size_t shift = space - outLen; size_t shift = space - outLen;
outLen += shift; outLen += shift;
_sentLength += shift; _sentLength += shift;
@@ -213,14 +268,14 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
} }
} }
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) {
(void)time; (void)time;
_ackedLength += len; _ackedLength += len;
if(_state == RESPONSE_CONTENT){ if (_state == RESPONSE_CONTENT) {
size_t available = _contentLength - _sentLength; size_t available = _contentLength - _sentLength;
size_t space = request->client()->space(); size_t space = request->client()->space();
//we can fit in this packet //we can fit in this packet
if(space > available){ if (space > available) {
_writtenLength += request->client()->write(_content.c_str(), available); _writtenLength += request->client()->write(_content.c_str(), available);
_content = String(); _content = String();
_state = RESPONSE_WAIT_ACK; _state = RESPONSE_WAIT_ACK;
@@ -232,8 +287,8 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint
_sentLength += space; _sentLength += space;
_writtenLength += request->client()->write(out.c_str(), space); _writtenLength += request->client()->write(out.c_str(), space);
return space; return space;
} else if(_state == RESPONSE_WAIT_ACK){ } else if (_state == RESPONSE_WAIT_ACK) {
if(_ackedLength >= _writtenLength){ if (_ackedLength >= _writtenLength) {
_state = RESPONSE_END; _state = RESPONSE_END;
} }
} }
@@ -245,26 +300,26 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint
* Abstract Response * Abstract Response
* */ * */
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback) AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback)
{ : _callback(callback) {
// In case of template processing, we're unable to determine real response size // In case of template processing, we're unable to determine real response size
if(callback) { if (callback) {
_contentLength = 0; _contentLength = 0;
_sendContentLength = false; _sendContentLength = false;
_chunked = true; _chunked = true;
} }
} }
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){ void AsyncAbstractResponse::_respond(AsyncWebServerRequest * request) {
addHeader(F("Connection"), F("close")); addHeader(F("Connection"), F("close"));
_head = _assembleHead(request->version()); _head = _assembleHead(request->version());
_state = RESPONSE_HEADERS; _state = RESPONSE_HEADERS;
_ack(request, 0, 0); _ack(request, 0, 0);
} }
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) {
(void)time; (void)time;
if(!_sourceValid()){ if (!_sourceValid()) {
_state = RESPONSE_FAILED; _state = RESPONSE_FAILED;
request->client()->close(); request->client()->close();
return 0; return 0;
@@ -273,8 +328,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
size_t space = request->client()->space(); size_t space = request->client()->space();
size_t headLen = _head.length(); size_t headLen = _head.length();
if(_state == RESPONSE_HEADERS){ if (_state == RESPONSE_HEADERS) {
if(space >= headLen){ if (space >= headLen) {
_state = RESPONSE_CONTENT; _state = RESPONSE_CONTENT;
space -= headLen; space -= headLen;
} else { } else {
@@ -285,64 +340,65 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
} }
} }
if(_state == RESPONSE_CONTENT){ if (_state == RESPONSE_CONTENT) {
size_t outLen; size_t outLen;
if(_chunked){ if (_chunked) {
if(space <= 8){ if (space <= 8) {
return 0; return 0;
} }
outLen = space; outLen = space;
} else if(!_sendContentLength){ } else if (!_sendContentLength) {
outLen = space; outLen = space;
} else { } else {
outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength); outLen = ((_contentLength - _sentLength) > space) ? space : (_contentLength - _sentLength);
} }
uint8_t *buf = (uint8_t *)malloc(outLen+headLen); uint8_t * buf = (uint8_t *)malloc(outLen + headLen);
if (!buf) { if (!buf) {
// os_printf("_ack malloc %d failed\n", outLen+headLen); // os_printf("_ack malloc %d failed\n", outLen+headLen);
return 0; return 0;
} }
if(headLen){ if (headLen) {
memcpy(buf, _head.c_str(), _head.length()); memcpy(buf, _head.c_str(), _head.length());
} }
size_t readLen = 0; size_t readLen = 0;
if(_chunked){ if (_chunked) {
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
// See RFC2616 sections 2, 3.6.1. // See RFC2616 sections 2, 3.6.1.
readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8); readLen = _fillBufferAndProcessTemplates(buf + headLen + 6, outLen - 8);
if(readLen == RESPONSE_TRY_AGAIN){ if (readLen == RESPONSE_TRY_AGAIN) {
free(buf); free(buf);
return 0; return 0;
} }
outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen; outLen = sprintf_P((char *)buf + headLen, PSTR("%x"), readLen) + headLen;
while(outLen < headLen + 4) buf[outLen++] = ' '; while (outLen < headLen + 4)
buf[outLen++] = ' ';
buf[outLen++] = '\r'; buf[outLen++] = '\r';
buf[outLen++] = '\n'; buf[outLen++] = '\n';
outLen += readLen; outLen += readLen;
buf[outLen++] = '\r'; buf[outLen++] = '\r';
buf[outLen++] = '\n'; buf[outLen++] = '\n';
} else { } else {
readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen); readLen = _fillBufferAndProcessTemplates(buf + headLen, outLen);
if(readLen == RESPONSE_TRY_AGAIN){ if (readLen == RESPONSE_TRY_AGAIN) {
free(buf); free(buf);
return 0; return 0;
} }
outLen = readLen + headLen; outLen = readLen + headLen;
} }
if(headLen){ if (headLen) {
_head = String(); _head = String();
} }
if(outLen){ if (outLen) {
_writtenLength += request->client()->write((const char*)buf, outLen); _writtenLength += request->client()->write((const char *)buf, outLen);
} }
if(_chunked){ if (_chunked) {
_sentLength += readLen; _sentLength += readLen;
} else { } else {
_sentLength += outLen - headLen; _sentLength += outLen - headLen;
@@ -350,26 +406,25 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
free(buf); free(buf);
if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){ if ((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)) {
_state = RESPONSE_WAIT_ACK; _state = RESPONSE_WAIT_ACK;
} }
return outLen; return outLen;
} else if(_state == RESPONSE_WAIT_ACK){ } else if (_state == RESPONSE_WAIT_ACK) {
if(!_sendContentLength || _ackedLength >= _writtenLength){ if (!_sendContentLength || _ackedLength >= _writtenLength) {
_state = RESPONSE_END; _state = RESPONSE_END;
if(!_chunked && !_sendContentLength) if (!_chunked && !_sendContentLength)
request->client()->close(true); request->client()->close(true);
} }
} }
return 0; return 0;
} }
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t * data, const size_t len) {
{
// If we have something in cache, copy it to buffer // If we have something in cache, copy it to buffer
const size_t readFromCache = std::min(len, _cache.size()); const size_t readFromCache = std::min(len, _cache.size());
if(readFromCache) { if (readFromCache) {
memcpy(data, _cache.data(), readFromCache); memcpy(data, _cache.data(), readFromCache);
_cache.erase(_cache.begin(), _cache.begin() + readFromCache); _cache.erase(_cache.begin(), _cache.begin() + readFromCache);
} }
@@ -379,87 +434,86 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s
return readFromCache + readFromContent; return readFromCache + readFromContent;
} }
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t * data, size_t len) {
{ if (!_callback)
if(!_callback)
return _fillBuffer(data, len); return _fillBuffer(data, len);
const size_t originalLen = len; const size_t originalLen = len;
len = _readDataFromCacheOrContent(data, len); len = _readDataFromCacheOrContent(data, len);
// Now we've read 'len' bytes, either from cache or from file // Now we've read 'len' bytes, either from cache or from file
// Search for template placeholders // Search for template placeholders
uint8_t* pTemplateStart = data; uint8_t * pTemplateStart = data;
while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] while ((pTemplateStart < &data[len])
uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; && (pTemplateStart = (uint8_t *)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
uint8_t * pTemplateEnd =
(pTemplateStart < &data[len - 1]) ? (uint8_t *)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
// temporary buffer to hold parameter name // temporary buffer to hold parameter name
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
String paramName; String paramName;
// If closing placeholder is found: // If closing placeholder is found:
if(pTemplateEnd) { if (pTemplateEnd) {
// prepare argument to callback // prepare argument to callback
const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1)); const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1));
if(paramNameLength) { if (paramNameLength) {
memcpy(buf, pTemplateStart + 1, paramNameLength); memcpy(buf, pTemplateStart + 1, paramNameLength);
buf[paramNameLength] = 0; buf[paramNameLength] = 0;
paramName = String(reinterpret_cast<char*>(buf)); paramName = String(reinterpret_cast<char *>(buf));
} else { // double percent sign encountered, this is single percent sign escaped. } else { // double percent sign encountered, this is single percent sign escaped.
// remove the 2nd percent sign // remove the 2nd percent sign
memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
++pTemplateStart; ++pTemplateStart;
} }
} else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data } else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); const size_t readFromCacheOrContent =
if(readFromCacheOrContent) { _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); if (readFromCacheOrContent) {
if(pTemplateEnd) { pTemplateEnd = (uint8_t *)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
if (pTemplateEnd) {
// prepare argument to callback // prepare argument to callback
*pTemplateEnd = 0; *pTemplateEnd = 0;
paramName = String(reinterpret_cast<char*>(buf)); paramName = String(reinterpret_cast<char *>(buf));
// Copy remaining read-ahead data into cache // Copy remaining read-ahead data into cache
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
pTemplateEnd = &data[len - 1]; pTemplateEnd = &data[len - 1];
} } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
{ {
// but first, store read file data in cache // but first, store read file data in cache
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
++pTemplateStart; ++pTemplateStart;
} }
} } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
++pTemplateStart; ++pTemplateStart;
} } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
++pTemplateStart; ++pTemplateStart;
if(paramName.length()) { if (paramName.length()) {
// call callback and replace with result. // call callback and replace with result.
// Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
// Data after pTemplateEnd may need to be moved. // Data after pTemplateEnd may need to be moved.
// The first byte of data after placeholder is located at pTemplateEnd + 1. // The first byte of data after placeholder is located at pTemplateEnd + 1.
// It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value). // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
const String paramValue(_callback(paramName)); const String paramValue(_callback(paramName));
const char* pvstr = paramValue.c_str(); const char * pvstr = paramValue.c_str();
const unsigned int pvlen = paramValue.length(); const unsigned int pvlen = paramValue.length();
const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1)); const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
// make room for param value // make room for param value
// 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { if ((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
_cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
//2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
len = originalLen; // fix issue with truncated data, not sure if it has any side effects len = originalLen; // fix issue with truncated data, not sure if it has any side effects
} else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied) } else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
//2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
// Move the entire data after the placeholder // Move the entire data after the placeholder
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
// 3. replace placeholder with actual value // 3. replace placeholder with actual value
memcpy(pTemplateStart, pvstr, numBytesCopied); memcpy(pTemplateStart, pvstr, numBytesCopied);
// If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
if(numBytesCopied < pvlen) { if (numBytesCopied < pvlen) {
_cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
} else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... } else if (pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
// there is some free room, fill it from cache // there is some free room, fill it from cache
const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
const size_t totalFreeRoom = originalLen - len + roomFreed; const size_t totalFreeRoom = originalLen - len + roomFreed;
@@ -478,43 +532,63 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
* File Response * File Response
* */ * */
AsyncFileResponse::~AsyncFileResponse(){ AsyncFileResponse::~AsyncFileResponse() {
if(_content) if (_content)
_content.close(); _content.close();
} }
void AsyncFileResponse::_setContentType(const String& path){ void AsyncFileResponse::_setContentType(const String & path) {
#if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION #if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION
extern const __FlashStringHelper *getContentType(const String &path); extern const __FlashStringHelper * getContentType(const String & path);
_contentType = getContentType(path); _contentType = getContentType(path);
#else #else
if (path.endsWith(F(".html"))) _contentType = F("text/html"); if (path.endsWith(F(".html")))
else if (path.endsWith(F(".htm"))) _contentType = F("text/html"); _contentType = F("text/html");
else if (path.endsWith(F(".css"))) _contentType = F("text/css"); else if (path.endsWith(F(".htm")))
else if (path.endsWith(F(".json"))) _contentType = F("application/json"); _contentType = F("text/html");
else if (path.endsWith(F(".js"))) _contentType = F("application/javascript"); else if (path.endsWith(F(".css")))
else if (path.endsWith(F(".png"))) _contentType = F("image/png"); _contentType = F("text/css");
else if (path.endsWith(F(".gif"))) _contentType = F("image/gif"); else if (path.endsWith(F(".json")))
else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg"); _contentType = F("application/json");
else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon"); else if (path.endsWith(F(".js")))
else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml"); _contentType = F("application/javascript");
else if (path.endsWith(F(".eot"))) _contentType = F("font/eot"); else if (path.endsWith(F(".png")))
else if (path.endsWith(F(".woff"))) _contentType = F("font/woff"); _contentType = F("image/png");
else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2"); else if (path.endsWith(F(".gif")))
else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf"); _contentType = F("image/gif");
else if (path.endsWith(F(".xml"))) _contentType = F("text/xml"); else if (path.endsWith(F(".jpg")))
else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf"); _contentType = F("image/jpeg");
else if (path.endsWith(F(".zip"))) _contentType = F("application/zip"); else if (path.endsWith(F(".ico")))
else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip"); _contentType = F("image/x-icon");
else _contentType = F("text/plain"); else if (path.endsWith(F(".svg")))
_contentType = F("image/svg+xml");
else if (path.endsWith(F(".eot")))
_contentType = F("font/eot");
else if (path.endsWith(F(".woff")))
_contentType = F("font/woff");
else if (path.endsWith(F(".woff2")))
_contentType = F("font/woff2");
else if (path.endsWith(F(".ttf")))
_contentType = F("font/ttf");
else if (path.endsWith(F(".xml")))
_contentType = F("text/xml");
else if (path.endsWith(F(".pdf")))
_contentType = F("application/pdf");
else if (path.endsWith(F(".zip")))
_contentType = F("application/zip");
else if (path.endsWith(F(".gz")))
_contentType = F("application/x-gzip");
else
_contentType = F("text/plain");
#endif #endif
} }
AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ AsyncFileResponse::AsyncFileResponse(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback)
: AsyncAbstractResponse(callback) {
_code = 200; _code = 200;
_path = path; _path = path;
if(!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))){ if (!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))) {
_path = _path + F(".gz"); _path = _path + F(".gz");
addHeader(F("Content-Encoding"), F("gzip")); addHeader(F("Content-Encoding"), F("gzip"));
_callback = nullptr; // Unable to process zipped templates _callback = nullptr; // Unable to process zipped templates
@@ -525,30 +599,31 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& c
_content = fs.open(_path, fs::FileOpenMode::read); _content = fs.open(_path, fs::FileOpenMode::read);
_contentLength = _content.size(); _contentLength = _content.size();
if(contentType.length() == 0) if (contentType.length() == 0)
_setContentType(path); _setContentType(path);
else else
_contentType = contentType; _contentType = contentType;
int filenameStart = path.lastIndexOf('/') + 1; int filenameStart = path.lastIndexOf('/') + 1;
char buf[26+path.length()-filenameStart]; char buf[26 + path.length() - filenameStart];
char* filename = (char*)path.c_str() + filenameStart; char * filename = (char *)path.c_str() + filenameStart;
if(download) { if (download) {
// set filename and force download // set filename and force download
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename); snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename);
} else { } else {
// set filename and force rendering // set filename and force rendering
snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename); snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename);
} }
addHeader(F("Content-Disposition"), buf); addHeader(F("Content-Disposition"), buf);
} }
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ AsyncFileResponse::AsyncFileResponse(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback)
: AsyncAbstractResponse(callback) {
_code = 200; _code = 200;
_path = path; _path = path;
if(!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))){ if (!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))) {
addHeader(F("Content-Encoding"), F("gzip")); addHeader(F("Content-Encoding"), F("gzip"));
_callback = nullptr; // Unable to process gzipped templates _callback = nullptr; // Unable to process gzipped templates
_sendContentLength = true; _sendContentLength = true;
@@ -558,24 +633,24 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const Str
_content = content; _content = content;
_contentLength = _content.size(); _contentLength = _content.size();
if(contentType.length() == 0) if (contentType.length() == 0)
_setContentType(path); _setContentType(path);
else else
_contentType = contentType; _contentType = contentType;
int filenameStart = path.lastIndexOf('/') + 1; int filenameStart = path.lastIndexOf('/') + 1;
char buf[26+path.length()-filenameStart]; char buf[26 + path.length() - filenameStart];
char* filename = (char*)path.c_str() + filenameStart; char * filename = (char *)path.c_str() + filenameStart;
if(download) { if (download) {
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename); snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename);
} else { } else {
snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename); snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename);
} }
addHeader(F("Content-Disposition"), buf); addHeader(F("Content-Disposition"), buf);
} }
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncFileResponse::_fillBuffer(uint8_t * data, size_t len) {
return _content.read(data, len); return _content.read(data, len);
} }
@@ -583,18 +658,19 @@ size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
* Stream Response * Stream Response
* */ * */
AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { AsyncStreamResponse::AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback)
: AsyncAbstractResponse(callback) {
_code = 200; _code = 200;
_content = &stream; _content = &stream;
_contentLength = len; _contentLength = len;
_contentType = contentType; _contentType = contentType;
} }
size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncStreamResponse::_fillBuffer(uint8_t * data, size_t len) {
size_t available = _content->available(); size_t available = _content->available();
size_t outLen = (available > len)?len:available; size_t outLen = (available > len) ? len : available;
size_t i; size_t i;
for(i=0;i<outLen;i++) for (i = 0; i < outLen; i++)
data[i] = _content->read(); data[i] = _content->read();
return outLen; return outLen;
} }
@@ -603,19 +679,20 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
* Callback Response * Callback Response
* */ * */
AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) { AsyncCallbackResponse::AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback)
: AsyncAbstractResponse(templateCallback) {
_code = 200; _code = 200;
_content = callback; _content = callback;
_contentLength = len; _contentLength = len;
if(!len) if (!len)
_sendContentLength = false; _sendContentLength = false;
_contentType = contentType; _contentType = contentType;
_filledLength = 0; _filledLength = 0;
} }
size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncCallbackResponse::_fillBuffer(uint8_t * data, size_t len) {
size_t ret = _content(data, len, _filledLength); size_t ret = _content(data, len, _filledLength);
if(ret != RESPONSE_TRY_AGAIN){ if (ret != RESPONSE_TRY_AGAIN) {
_filledLength += ret; _filledLength += ret;
} }
return ret; return ret;
@@ -625,7 +702,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
* Chunked Response * Chunked Response
* */ * */
AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) { AsyncChunkedResponse::AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback)
: AsyncAbstractResponse(processorCallback) {
_code = 200; _code = 200;
_content = callback; _content = callback;
_contentLength = 0; _contentLength = 0;
@@ -635,9 +713,9 @@ AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsRespons
_filledLength = 0; _filledLength = 0;
} }
size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncChunkedResponse::_fillBuffer(uint8_t * data, size_t len) {
size_t ret = _content(data, len, _filledLength); size_t ret = _content(data, len, _filledLength);
if(ret != RESPONSE_TRY_AGAIN){ if (ret != RESPONSE_TRY_AGAIN) {
_filledLength += ret; _filledLength += ret;
} }
return ret; return ret;
@@ -647,7 +725,8 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
* Progmem Response * Progmem Response
* */ * */
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { AsyncProgmemResponse::AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback)
: AsyncAbstractResponse(callback) {
_code = code; _code = code;
_content = content; _content = content;
_contentType = contentType; _contentType = contentType;
@@ -655,7 +734,7 @@ AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType,
_readLength = 0; _readLength = 0;
} }
size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncProgmemResponse::_fillBuffer(uint8_t * data, size_t len) {
size_t left = _contentLength - _readLength; size_t left = _contentLength - _readLength;
if (left > len) { if (left > len) {
memcpy_P(data, _content + _readLength, len); memcpy_P(data, _content + _readLength, len);
@@ -667,13 +746,11 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
return left; return left;
} }
/* /*
* Response Stream (You can print/write/printf to it, up to the contentLen bytes) * Response Stream (You can print/write/printf to it, up to the contentLen bytes)
* */ * */
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize) AsyncResponseStream::AsyncResponseStream(const String & contentType, size_t bufferSize) {
{
_code = 200; _code = 200;
_contentLength = 0; _contentLength = 0;
_contentType = contentType; _contentType = contentType;
@@ -682,23 +759,23 @@ AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t buffe
AsyncResponseStream::~AsyncResponseStream() = default; AsyncResponseStream::~AsyncResponseStream() = default;
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){ size_t AsyncResponseStream::_fillBuffer(uint8_t * buf, size_t maxLen) {
return _content->read((char*)buf, maxLen); return _content->read((char *)buf, maxLen);
} }
size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ size_t AsyncResponseStream::write(const uint8_t * data, size_t len) {
if(_started()) if (_started())
return 0; return 0;
if(len > _content->room()){ if (len > _content->room()) {
size_t needed = len - _content->room(); size_t needed = len - _content->room();
_content->resizeAdd(needed); _content->resizeAdd(needed);
} }
size_t written = _content->write((const char*)data, len); size_t written = _content->write((const char *)data, len);
_contentLength += written; _contentLength += written;
return written; return written;
} }
size_t AsyncResponseStream::write(uint8_t data){ size_t AsyncResponseStream::write(uint8_t data) {
return write(&data, 1); return write(&data, 1);
} }

View File

@@ -20,105 +20,108 @@
*/ */
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
#include "WebHandlerImpl.h" #include "WebHandlerImpl.h"
#include "AsyncJson.h"
bool ON_STA_FILTER(AsyncWebServerRequest *request) { bool ON_STA_FILTER(AsyncWebServerRequest * request) {
return WiFi.localIP() == request->client()->localIP(); return WiFi.localIP() == request->client()->localIP();
} }
bool ON_AP_FILTER(AsyncWebServerRequest *request) { bool ON_AP_FILTER(AsyncWebServerRequest * request) {
return WiFi.localIP() != request->client()->localIP(); return WiFi.localIP() != request->client()->localIP();
} }
#ifndef HAVE_FS_FILE_OPEN_MODE #ifndef HAVE_FS_FILE_OPEN_MODE
const char *fs::FileOpenMode::read = "r"; const char * fs::FileOpenMode::read = "r";
const char *fs::FileOpenMode::write = "w"; const char * fs::FileOpenMode::write = "w";
const char *fs::FileOpenMode::append = "a"; const char * fs::FileOpenMode::append = "a";
#endif #endif
AsyncWebServer::AsyncWebServer(uint16_t port) AsyncWebServer::AsyncWebServer(uint16_t port)
: _server(port) : _server(port)
, _rewrites(LinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; })) , _rewrites(LinkedList<AsyncWebRewrite *>([](AsyncWebRewrite * r) { delete r; }))
, _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; })) , _handlers(LinkedList<AsyncWebHandler *>([](AsyncWebHandler * h) { delete h; })) {
{
_catchAllHandler = new AsyncCallbackWebHandler(); _catchAllHandler = new AsyncCallbackWebHandler();
if(_catchAllHandler == NULL) if (_catchAllHandler == NULL)
return; return;
_server.onClient([](void *s, AsyncClient* c){ _server.onClient(
if(c == NULL) [](void * s, AsyncClient * c) {
if (c == NULL)
return; return;
c->setRxTimeout(3); c->setRxTimeout(3);
AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c); AsyncWebServerRequest * r = new AsyncWebServerRequest((AsyncWebServer *)s, c);
if(r == NULL){ if (r == NULL) {
c->close(true); c->close(true);
c->free(); c->free();
delete c; delete c;
} }
}, this); },
this);
} }
AsyncWebServer::~AsyncWebServer(){ AsyncWebServer::~AsyncWebServer() {
reset(); reset();
end(); end();
if(_catchAllHandler) delete _catchAllHandler; if (_catchAllHandler)
delete _catchAllHandler;
} }
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){ AsyncWebRewrite & AsyncWebServer::addRewrite(AsyncWebRewrite * rewrite) {
_rewrites.add(rewrite); _rewrites.add(rewrite);
return *rewrite; return *rewrite;
} }
bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){ bool AsyncWebServer::removeRewrite(AsyncWebRewrite * rewrite) {
return _rewrites.remove(rewrite); return _rewrites.remove(rewrite);
} }
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){ AsyncWebRewrite & AsyncWebServer::rewrite(const char * from, const char * to) {
return addRewrite(new AsyncWebRewrite(from, to)); return addRewrite(new AsyncWebRewrite(from, to));
} }
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){ AsyncWebHandler & AsyncWebServer::addHandler(AsyncWebHandler * handler) {
_handlers.add(handler); _handlers.add(handler);
return *handler; return *handler;
} }
bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){ bool AsyncWebServer::removeHandler(AsyncWebHandler * handler) {
return _handlers.remove(handler); return _handlers.remove(handler);
} }
void AsyncWebServer::begin(){ void AsyncWebServer::begin() {
_server.setNoDelay(true); _server.setNoDelay(true);
_server.begin(); _server.begin();
} }
void AsyncWebServer::end(){ void AsyncWebServer::end() {
_server.end(); _server.end();
} }
#if ASYNC_TCP_SSL_ENABLED #if ASYNC_TCP_SSL_ENABLED
void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){ void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void * arg) {
_server.onSslFileRequest(cb, arg); _server.onSslFileRequest(cb, arg);
} }
void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){ void AsyncWebServer::beginSecure(const char * cert, const char * key, const char * password) {
_server.beginSecure(cert, key, password); _server.beginSecure(cert, key, password);
} }
#endif #endif
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){ void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest * request) {
delete request; delete request;
} }
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){ void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest * request) {
for(const auto& r: _rewrites){ for (const auto & r : _rewrites) {
if (r->match(request)){ if (r->match(request)) {
request->_url = r->toUrl(); request->_url = r->toUrl();
request->_addGetParams(r->params()); request->_addGetParams(r->params());
} }
} }
} }
void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){ void AsyncWebServer::_attachHandler(AsyncWebServerRequest * request) {
for(const auto& h: _handlers){ for (const auto & h : _handlers) {
if (h->filter(request) && h->canHandle(request)){ if (h->filter(request) && h->canHandle(request)) {
request->setHandler(h); request->setHandler(h);
return; return;
} }
@@ -129,8 +132,12 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
} }
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){ AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri,
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); WebRequestMethodComposite method,
ArRequestHandlerFunction onRequest,
ArUploadHandlerFunction onUpload,
ArBodyHandlerFunction onBody) {
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
handler->setUri(uri); handler->setUri(uri);
handler->setMethod(method); handler->setMethod(method);
handler->onRequest(onRequest); handler->onRequest(onRequest);
@@ -140,8 +147,9 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
return *handler; return *handler;
} }
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){ AsyncCallbackWebHandler &
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); AsyncWebServer::on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) {
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
handler->setUri(uri); handler->setUri(uri);
handler->setMethod(method); handler->setMethod(method);
handler->onRequest(onRequest); handler->onRequest(onRequest);
@@ -150,8 +158,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
return *handler; return *handler;
} }
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){ AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) {
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
handler->setUri(uri); handler->setUri(uri);
handler->setMethod(method); handler->setMethod(method);
handler->onRequest(onRequest); handler->onRequest(onRequest);
@@ -159,40 +167,45 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
return *handler; return *handler;
} }
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){ AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, ArRequestHandlerFunction onRequest) {
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
handler->setUri(uri); handler->setUri(uri);
handler->onRequest(onRequest); handler->onRequest(onRequest);
addHandler(handler); addHandler(handler);
return *handler; return *handler;
} }
AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){ // added by proddy
AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); void AsyncWebServer::on(const char * uri, ArJsonRequestHandlerFunction onRequest) {
auto * handler = new AsyncCallbackJsonWebHandler(uri, onRequest);
addHandler(handler);
}
AsyncStaticWebHandler & AsyncWebServer::serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control) {
AsyncStaticWebHandler * handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
addHandler(handler); addHandler(handler);
return *handler; return *handler;
} }
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){ void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn) {
_catchAllHandler->onRequest(fn); _catchAllHandler->onRequest(fn);
} }
void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){ void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn) {
_catchAllHandler->onUpload(fn); _catchAllHandler->onUpload(fn);
} }
void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){ void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) {
_catchAllHandler->onBody(fn); _catchAllHandler->onBody(fn);
} }
void AsyncWebServer::reset(){ void AsyncWebServer::reset() {
_rewrites.free(); _rewrites.free();
_handlers.free(); _handlers.free();
if (_catchAllHandler != NULL){ if (_catchAllHandler != NULL) {
_catchAllHandler->onRequest(NULL); _catchAllHandler->onRequest(NULL);
_catchAllHandler->onUpload(NULL); _catchAllHandler->onUpload(NULL);
_catchAllHandler->onBody(NULL); _catchAllHandler->onBody(NULL);
} }
} }

View File

@@ -1,12 +1,9 @@
#include "AuthenticationService.h" #include "AuthenticationService.h"
AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager) AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager)
: _securityManager(securityManager) : _securityManager(securityManager) {
, _signInHandler(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); }) {
server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { verifyAuthorization(request); }); server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { verifyAuthorization(request); });
_signInHandler.setMethod(HTTP_POST); server->on(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); });
_signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
server->addHandler(&_signInHandler);
} }
/** /**

View File

@@ -9,17 +9,13 @@
#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization" #define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization"
#define SIGN_IN_PATH "/rest/signIn" #define SIGN_IN_PATH "/rest/signIn"
#define MAX_AUTHENTICATION_SIZE 256
class AuthenticationService { class AuthenticationService {
public: public:
AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager); AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager);
private: private:
SecurityManager * _securityManager; SecurityManager * _securityManager;
AsyncCallbackJsonWebHandler _signInHandler;
// endpoint functions
void signIn(AsyncWebServerRequest * request, JsonVariant json); void signIn(AsyncWebServerRequest * request, JsonVariant json);
void verifyAuthorization(AsyncWebServerRequest * request); void verifyAuthorization(AsyncWebServerRequest * request);
}; };

View File

@@ -27,7 +27,7 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
static char last_modified[50]; static char last_modified[50];
sprintf(last_modified, "%s %s CET", __DATE__, __TIME__); sprintf(last_modified, "%s %s CET", __DATE__, __TIME__);
WWWData::registerRoutes([server](const String & uri, const String & contentType, const uint8_t * content, size_t len, const String & hash) { WWWData::registerRoutes([server](const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash) {
ArRequestHandlerFunction requestHandler = [contentType, content, len, hash](AsyncWebServerRequest * request) { ArRequestHandlerFunction requestHandler = [contentType, content, len, hash](AsyncWebServerRequest * request) {
// Check if the client already has the same version and respond with a 304 (Not modified) // Check if the client already has the same version and respond with a 304 (Not modified)
if (request->header("If-Modified-Since").indexOf(last_modified) > 0) { if (request->header("If-Modified-Since").indexOf(last_modified) > 0) {
@@ -37,6 +37,7 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
} }
AsyncWebServerResponse * response = request->beginResponse_P(200, contentType, content, len); AsyncWebServerResponse * response = request->beginResponse_P(200, contentType, content, len);
response->addHeader("Content-Encoding", "gzip"); response->addHeader("Content-Encoding", "gzip");
// response->addHeader("Content-Encoding", "br"); // only works over HTTPS // response->addHeader("Content-Encoding", "br"); // only works over HTTPS
// response->addHeader("Cache-Control", "public, immutable, max-age=31536000"); // response->addHeader("Cache-Control", "public, immutable, max-age=31536000");
@@ -46,10 +47,10 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
request->send(response); request->send(response);
}; };
server->on(uri.c_str(), HTTP_GET, requestHandler); server->on(uri, HTTP_GET, requestHandler);
// Serving non matching get requests with "/index.html" // Serving non matching get requests with "/index.html"
// OPTIONS get a straight up 200 response // OPTIONS get a straight up 200 response
if (uri.equals("/index.html")) { if (strncmp(uri, "/index.html", 11) == 0) {
server->onNotFound([requestHandler](AsyncWebServerRequest * request) { server->onNotFound([requestHandler](AsyncWebServerRequest * request) {
if (request->method() == HTTP_GET) { if (request->method() == HTTP_GET) {
requestHandler(request); requestHandler(request);

View File

@@ -32,8 +32,11 @@ class FSPersistence {
settingsFile.close(); settingsFile.close();
} }
// If we reach here we have not been successful in loading the config, // If we reach here we have not been successful in loading the config,
// hard-coded emergency defaults are now applied. // hard-coded emergency defaults are now applied.
#ifdef EMSESP_DEBUG
Serial.println("Applying defaults to " + String(_filePath));
#endif
applyDefaults(); applyDefaults();
writeToFS(); // added to make sure the initial file is created writeToFS(); // added to make sure the initial file is created
} }

View File

@@ -17,8 +17,6 @@ class HttpEndpoint {
JsonStateUpdater<T> _stateUpdater; JsonStateUpdater<T> _stateUpdater;
StatefulService<T> * _statefulService; StatefulService<T> * _statefulService;
AsyncCallbackJsonWebHandler * handler;
public: public:
HttpEndpoint(JsonStateReader<T> stateReader, HttpEndpoint(JsonStateReader<T> stateReader,
JsonStateUpdater<T> stateUpdater, JsonStateUpdater<T> stateUpdater,
@@ -30,12 +28,10 @@ class HttpEndpoint {
: _stateReader(stateReader) : _stateReader(stateReader)
, _stateUpdater(stateUpdater) , _stateUpdater(stateUpdater)
, _statefulService(statefulService) { , _statefulService(statefulService) {
// Create hander for both GET and POST endpoints // Create handler for both GET and POST endpoints
handler = new AsyncCallbackJsonWebHandler(servicePath, server->on(servicePath.c_str(),
securityManager->wrapCallback([this](AsyncWebServerRequest * request, securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { handleRequest(request, json); },
JsonVariant json) { handleRequest(request, json); },
authenticationPredicate)); authenticationPredicate));
server->addHandler(handler);
} }
protected: protected:

View File

@@ -5,13 +5,10 @@
NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager) : _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager)
, _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE) , _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE)
, _timeHandler(TIME_PATH,
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { configureTime(request, json); },
AuthenticationPredicates::IS_ADMIN))
, _connected(false) { , _connected(false) {
_timeHandler.setMethod(HTTP_POST); server->on(TIME_PATH,
_timeHandler.setMaxContentLength(MAX_TIME_SIZE); securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { configureTime(request, json); },
server->addHandler(&_timeHandler); AuthenticationPredicates::IS_ADMIN));
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); }); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
addUpdateHandler([this] { configureNTP(); }, false); addUpdateHandler([this] { configureNTP(); }, false);

View File

@@ -24,9 +24,8 @@
#endif #endif
#define NTP_SETTINGS_FILE "/config/ntpSettings.json" #define NTP_SETTINGS_FILE "/config/ntpSettings.json"
#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
#define MAX_TIME_SIZE 256 #define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
#define TIME_PATH "/rest/time" #define TIME_PATH "/rest/time"
class NTPSettings { class NTPSettings {
@@ -50,7 +49,6 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
private: private:
HttpEndpoint<NTPSettings> _httpEndpoint; HttpEndpoint<NTPSettings> _httpEndpoint;
FSPersistence<NTPSettings> _fsPersistence; FSPersistence<NTPSettings> _fsPersistence;
AsyncCallbackJsonWebHandler _timeHandler;
bool _connected; bool _connected;
void WiFiEvent(WiFiEvent_t event); void WiFiEvent(WiFiEvent_t event);

View File

@@ -368,13 +368,7 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
case ARDUINO_EVENT_ETH_GOT_IP6: case ARDUINO_EVENT_ETH_GOT_IP6:
if (emsesp::EMSESP::system_.ethernet_connected()) { if (emsesp::EMSESP::system_.ethernet_connected()) {
emsesp::EMSESP::logger().info("Ethernet connected (IPv6=%s, speed %d Mbps)", ETH.localIPv6().toString().c_str(), ETH.linkSpeed()); emsesp::EMSESP::logger().info("Ethernet connected (IPv6=%s, speed %d Mbps)", ETH.localIPv6().toString().c_str(), ETH.linkSpeed());
} else {
emsesp::EMSESP::logger().info("WiFi connected (IPv6=%s, hostname=%s, TxPower=%s dBm)",
WiFi.localIPv6().toString().c_str(),
WiFi.getHostname(),
emsesp::Helpers::render_value(result, ((double)(WiFi.getTxPower()) / 4), 1));
} }
mDNS_start();
emsesp::EMSESP::system_.has_ipv6(true); emsesp::EMSESP::system_.has_ipv6(true);
break; break;

View File

@@ -213,6 +213,7 @@ class AsyncWebServerResponse {
typedef std::function<void(AsyncWebServerRequest * request)> ArRequestHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request)> ArRequestHandlerFunction;
typedef std::function<void(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final)> ArUploadHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final)> ArUploadHandlerFunction;
typedef std::function<void(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction; typedef std::function<void(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant json)> ArJsonRequestHandlerFunction; // added by proddy
class AsyncWebServer { class AsyncWebServer {
protected: protected:
@@ -232,6 +233,7 @@ class AsyncWebServer {
} }
void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){}; void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){};
void on(const char * uri, ArJsonRequestHandlerFunction onRequest){}; // added by proddy
}; };

File diff suppressed because one or more lines are too long

View File

@@ -4,4 +4,4 @@ enableGlobalCache: false
nodeLinker: node-modules nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.1.0.cjs yarnPath: .yarn/releases/yarn-4.1.1.cjs

View File

@@ -12,11 +12,11 @@
"dependencies": { "dependencies": {
"@msgpack/msgpack": "^2.8.0", "@msgpack/msgpack": "^2.8.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"express": "^4.18.2", "express": "^4.18.3",
"itty-router": "^4.0.27", "itty-router": "^4.2.0",
"multer": "^1.4.5-lts.1" "multer": "^1.4.5-lts.1"
}, },
"packageManager": "yarn@4.1.0", "packageManager": "yarn@4.1.1",
"devDependencies": { "devDependencies": {
"@types/multer": "^1.4.11" "@types/multer": "^1.4.11"
} }

View File

@@ -139,8 +139,8 @@ __metadata:
"@msgpack/msgpack": "npm:^2.8.0" "@msgpack/msgpack": "npm:^2.8.0"
"@types/multer": "npm:^1.4.11" "@types/multer": "npm:^1.4.11"
compression: "npm:^1.7.4" compression: "npm:^1.7.4"
express: "npm:^4.18.2" express: "npm:^4.18.3"
itty-router: "npm:^4.0.27" itty-router: "npm:^4.2.0"
multer: "npm:^1.4.5-lts.1" multer: "npm:^1.4.5-lts.1"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@@ -159,12 +159,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"body-parser@npm:1.20.1": "body-parser@npm:1.20.2":
version: 1.20.1 version: 1.20.2
resolution: "body-parser@npm:1.20.1" resolution: "body-parser@npm:1.20.2"
dependencies: dependencies:
bytes: "npm:3.1.2" bytes: "npm:3.1.2"
content-type: "npm:~1.0.4" content-type: "npm:~1.0.5"
debug: "npm:2.6.9" debug: "npm:2.6.9"
depd: "npm:2.0.0" depd: "npm:2.0.0"
destroy: "npm:1.2.0" destroy: "npm:1.2.0"
@@ -172,10 +172,10 @@ __metadata:
iconv-lite: "npm:0.4.24" iconv-lite: "npm:0.4.24"
on-finished: "npm:2.4.1" on-finished: "npm:2.4.1"
qs: "npm:6.11.0" qs: "npm:6.11.0"
raw-body: "npm:2.5.1" raw-body: "npm:2.5.2"
type-is: "npm:~1.6.18" type-is: "npm:~1.6.18"
unpipe: "npm:1.0.0" unpipe: "npm:1.0.0"
checksum: 10/5f8d128022a2fb8b6e7990d30878a0182f300b70e46b3f9d358a9433ad6275f0de46add6d63206da3637c01c3b38b6111a7480f7e7ac2e9f7b989f6133fe5510 checksum: 10/3cf171b82190cf91495c262b073e425fc0d9e25cc2bf4540d43f7e7bbca27d6a9eae65ca367b6ef3993eea261159d9d2ab37ce444e8979323952e12eb3df319a
languageName: node languageName: node
linkType: hard linkType: hard
@@ -265,7 +265,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"content-type@npm:~1.0.4": "content-type@npm:~1.0.4, content-type@npm:~1.0.5":
version: 1.0.5 version: 1.0.5
resolution: "content-type@npm:1.0.5" resolution: "content-type@npm:1.0.5"
checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662 checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662
@@ -355,13 +355,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"express@npm:^4.18.2": "express@npm:^4.18.3":
version: 4.18.2 version: 4.18.3
resolution: "express@npm:4.18.2" resolution: "express@npm:4.18.3"
dependencies: dependencies:
accepts: "npm:~1.3.8" accepts: "npm:~1.3.8"
array-flatten: "npm:1.1.1" array-flatten: "npm:1.1.1"
body-parser: "npm:1.20.1" body-parser: "npm:1.20.2"
content-disposition: "npm:0.5.4" content-disposition: "npm:0.5.4"
content-type: "npm:~1.0.4" content-type: "npm:~1.0.4"
cookie: "npm:0.5.0" cookie: "npm:0.5.0"
@@ -390,7 +390,7 @@ __metadata:
type-is: "npm:~1.6.18" type-is: "npm:~1.6.18"
utils-merge: "npm:1.0.1" utils-merge: "npm:1.0.1"
vary: "npm:~1.1.2" vary: "npm:~1.1.2"
checksum: 10/869ae89ed6ff4bed7b373079dc58e5dddcf2915a2669b36037ff78c99d675ae930e5fe052b35c24f56557d28a023bb1cbe3e2f2fb87eaab96a1cedd7e597809d checksum: 10/0bf4656d0020cdc477aec884c6245dceea78992f6c747c899c87dbb0598474658d4130a29c80f02c99d1f0d6ebef706e7131c70c5454c3e07aba761c5bd3d627
languageName: node languageName: node
linkType: hard linkType: hard
@@ -526,10 +526,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"itty-router@npm:^4.0.27": "itty-router@npm:^4.2.0":
version: 4.0.27 version: 4.2.0
resolution: "itty-router@npm:4.0.27" resolution: "itty-router@npm:4.2.0"
checksum: 10/ebb959388b1033f3d80ba2575c2d90fa649c1d5370d977879513cc46e8fd78159b7140d2a66853af6be98f7d740f8609a2c5aa7381506eaa1f1a46268fd2a95f checksum: 10/39ee6c8b87f77de3918a9b3c1acaf2047626a69b954d7e1f5b9ab7ab9a2bf7e43c97b99ab86496aa9f5b139b024eebaf3b423f51fe000a8a0510901aeea10604
languageName: node languageName: node
linkType: hard linkType: hard
@@ -710,15 +710,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"raw-body@npm:2.5.1": "raw-body@npm:2.5.2":
version: 2.5.1 version: 2.5.2
resolution: "raw-body@npm:2.5.1" resolution: "raw-body@npm:2.5.2"
dependencies: dependencies:
bytes: "npm:3.1.2" bytes: "npm:3.1.2"
http-errors: "npm:2.0.0" http-errors: "npm:2.0.0"
iconv-lite: "npm:0.4.24" iconv-lite: "npm:0.4.24"
unpipe: "npm:1.0.0" unpipe: "npm:1.0.0"
checksum: 10/280bedc12db3490ecd06f740bdcf66093a07535374b51331242382c0e130bb273ebb611b7bc4cba1b4b4e016cc7b1f4b05a6df885a6af39c2bc3b94c02291c84 checksum: 10/863b5171e140546a4d99f349b720abac4410338e23df5e409cfcc3752538c9caf947ce382c89129ba976f71894bd38b5806c774edac35ebf168d02aa1ac11a95
languageName: node languageName: node
linkType: hard linkType: hard

View File

@@ -66,6 +66,8 @@ extra_scripts =
[env] [env]
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = direct, esp32_exception_decoder
upload_speed = 921600 upload_speed = 921600
build_type = release build_type = release
lib_ldf_mode = chain+ lib_ldf_mode = chain+
@@ -189,7 +191,7 @@ platform = native
build_flags = build_flags =
-DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
-DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.5-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.5-dev.16\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-lpthread -lpthread
-D__linux__ -D__linux__
-std=gnu++11 -Og -ggdb -std=gnu++11 -Og -ggdb

View File

@@ -4,7 +4,7 @@
# The response will be shown in the right panel # The response will be shown in the right panel
@host = http://ems-esp.local @host = http://ems-esp.local
@host_dev = http://ems-esp2.local @host_dev = http://10.10.10.20
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg @token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg
@@ -95,3 +95,14 @@ Authorization: Bearer {{token}}
### ###
GET {{host_dev}}/api/system/commands GET {{host_dev}}/api/system/commands
###
POST {{host_dev}}/api/thermostat/seltemp
Content-Type: application/json
Authorization: Bearer {{token}}
{
"value" : 21.0
}
###
GET {{host_dev}}/api/thermostat/seltemp

View File

@@ -4,6 +4,6 @@
# run from top folder like `sh ./scripts/dump_entities.sh` # run from top folder like `sh ./scripts/dump_entities.sh`
rm -f dump_entities.csv rm -f dump_entities.csv
make clean make clean
make ARGS=-DEMSESP_STANDALONE_DUMP make ARGS=-DEMSESP_STANDALONE
echo "test entity_dump" | ./emsesp | python3 ./scripts/dump_entities.py > dump_entities.csv echo "test entity_dump" | ./emsesp | python3 ./scripts/dump_entities.py > dump_entities.csv
cat dump_entities.csv cat dump_entities.csv

View File

@@ -198,8 +198,16 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
}); });
}); });
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, string_vector{F_(restart)}, [](Shell & shell, const std::vector<std::string> & arguments) { commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
string_vector{F_(restart)},
string_vector{F_(partitionname_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size()) {
to_app(shell).system_.system_restart(arguments.front().c_str());
} else {
to_app(shell).system_.system_restart(); to_app(shell).system_.system_restart();
}
}); });
commands->add_command(ShellContext::MAIN, commands->add_command(ShellContext::MAIN,
@@ -238,7 +246,8 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
networkSettings.password = password2.c_str(); networkSettings.password = password2.c_str();
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
}); });
shell.println("WiFi password updated"); shell.println("WiFi password updated. Reconnecting...");
to_app(shell).system_.wifi_reconnect();
} else { } else {
shell.println("Passwords do not match"); shell.println("Passwords do not match");
} }
@@ -271,7 +280,8 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
networkSettings.ssid = arguments.front().c_str(); networkSettings.ssid = arguments.front().c_str();
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
}); });
shell.println("WiFi ssid updated"); shell.println("WiFi ssid updated. Reconnecting...");
to_app(shell).system_.wifi_reconnect();
}); });
@@ -342,6 +352,43 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
to_app(shell).uart_init(); to_app(shell).uart_init();
}); });
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
string_vector{F_(set), F_(service)},
string_vector{F_(service_mandatory), F_(enable_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.back() == "enable" || arguments.back() == "disable") {
bool enable = arguments.back() == "enable";
if (arguments.front() == "mqtt") {
to_app(shell).esp8266React.getMqttSettingsService()->update([&](MqttSettings & Settings) {
Settings.enabled = enable;
return StateUpdateResult::CHANGED;
});
} else if (arguments.front() == "ota") {
to_app(shell).esp8266React.getOTASettingsService()->update([&](OTASettings & Settings) {
Settings.enabled = enable;
return StateUpdateResult::CHANGED;
});
} else if (arguments.front() == "ntp") {
to_app(shell).esp8266React.getNTPSettingsService()->update([&](NTPSettings & Settings) {
Settings.enabled = enable;
return StateUpdateResult::CHANGED;
});
} else if (arguments.front() == "ap") {
to_app(shell).esp8266React.getAPSettingsService()->update([&](APSettings & Settings) {
Settings.provisionMode = enable ? 0 : 2;
return StateUpdateResult::CHANGED;
});
} else {
shell.printfln("unknown service: %s", arguments.front().c_str());
return;
}
shell.printfln("service '%s' %sd", arguments.front().c_str(), arguments.back().c_str());
} else {
shell.println("Must be `enable` or `disable`");
}
});
// //
// EMS device commands // EMS device commands
// //
@@ -594,13 +641,15 @@ void EMSESPShell::stopped() {
// show welcome banner // show welcome banner
void EMSESPShell::display_banner() { void EMSESPShell::display_banner() {
println(); println();
printfln("┌───────────────────────────────────────┐"); printfln("┌──────────────────────────────────────────");
printfln("│ %sEMS-ESP version %-12s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF); printfln("│ %sEMS-ESP version %-12s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
printfln("│ │");
printfln("│ %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
printfln("│ %ssu%s to access admin commands │", COLOR_UNDERLINE, COLOR_RESET);
printfln("│ │");
printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET); printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
printfln("│ │"); printfln("│ │");
printfln("│ type %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET); printfln("└──────────────────────────────────────────┘");
printfln("│ use %ssu%s to access Admin commands │", COLOR_UNDERLINE, COLOR_RESET);
printfln("└───────────────────────────────────────┘");
println(); println();
// set console name // set console name

View File

@@ -239,7 +239,8 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const ui
return heating_circuit; return heating_circuit;
} }
} }
LOG_DEBUG("Heating circuit not fond on device 0x%02X", device_id());
LOG_DEBUG("Heating circuit not found on device 0x%02X", device_id());
return nullptr; // not found return nullptr; // not found
} }
@@ -3023,7 +3024,7 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
} }
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) { } else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) {
if (Helpers::value2enum(value, set, FL_(enum_controlmode))) { if (Helpers::value2enum(value, set, FL_(enum_controlmode))) {
write_command(curve_typeids[hc->hc()], 0, set, curve_typeids[hc->hc()]); write_command(curve_typeids[hc->hc()], 0, set + 1, curve_typeids[hc->hc()]);
return true; return true;
} }
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_BC400 || model() == EMSdevice::EMS_DEVICE_FLAG_RC300) { } else if (model() == EMSdevice::EMS_DEVICE_FLAG_BC400 || model() == EMSdevice::EMS_DEVICE_FLAG_RC300) {
@@ -4506,9 +4507,11 @@ void Thermostat::register_device_values() {
break; break;
} }
#if defined(EMSESP_STANDALONE_DUMP) #if defined(EMSESP_STANDALONE)
// if we're just dumping out values, create a single dummy hc // if we're just dumping out values, create a single dummy hc
register_device_values_hc(std::make_shared<Thermostat::HeatingCircuit>(1, this->model())); // hc=1 auto new_hc = std::make_shared<Thermostat::HeatingCircuit>(1, this->model()); // hc = 1
heating_circuits_.push_back(new_hc);
register_device_values_hc(new_hc);
#endif #endif
} }
@@ -4598,8 +4601,13 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
MAKE_CF_CB(set_summermode)); MAKE_CF_CB(set_summermode));
register_device_value(tag, &hc->summermode, DeviceValueType::ENUM, FL_(enum_summer), FL_(summermode), DeviceValueUOM::NONE); register_device_value(tag, &hc->summermode, DeviceValueType::ENUM, FL_(enum_summer), FL_(summermode), DeviceValueUOM::NONE);
register_device_value(tag, &hc->hpoperatingstate, DeviceValueType::ENUM, FL_(enum_operatingstate), FL_(hpoperatingstate), DeviceValueUOM::NONE); register_device_value(tag, &hc->hpoperatingstate, DeviceValueType::ENUM, FL_(enum_operatingstate), FL_(hpoperatingstate), DeviceValueUOM::NONE);
if (model == EMSdevice::EMS_DEVICE_FLAG_RC100) {
register_device_value(
tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
} else {
register_device_value( register_device_value(
tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode1), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode)); tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode1), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
}
register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
register_device_value(tag, register_device_value(tag,
&hc->tempautotemp, &hc->tempautotemp,

View File

@@ -1833,8 +1833,11 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_); EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_);
#endif #endif
// removing fetch causes issue: https://github.com/emsesp/EMS-ESP32/issues/1420 // removing fetch after start causes issue: https://github.com/emsesp/EMS-ESP32/issues/1420
// tf.fetch_ = false; // continue retry the first 5 minutes, then disable (added 15.3.2024)
if (uuid::get_uptime_sec() > 600) {
tf.fetch_ = false;
}
return false; return false;
} }
if (telegram->message_length > 0) { if (telegram->message_length > 0) {

View File

@@ -18,6 +18,10 @@
#include "emsesp.h" #include "emsesp.h"
#ifndef EMSESP_STANDALONE
#include "esp_ota_ops.h"
#endif
static_assert(uuid::thread_safe, "uuid-common must be thread-safe"); static_assert(uuid::thread_safe, "uuid-common must be thread-safe");
static_assert(uuid::log::thread_safe, "uuid-log must be thread-safe"); static_assert(uuid::log::thread_safe, "uuid-log must be thread-safe");
static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe"); static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe");
@@ -1496,11 +1500,14 @@ void EMSESP::start() {
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc) esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
if (!nvs_.begin("ems-esp", false, "nvs1")) { // try new partition on 16M flash first if (!nvs_.begin("ems-esp", false, "nvs1")) { // try bigger nvs partition on 16M flash first
nvs_.begin("ems-esp", false, "nvs"); // fallback to first nvs nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
} }
#ifndef EMSESP_STANDALONE
LOG_INFO("Starting EMS-ESP version %s from partition %s", EMSESP_APP_VERSION, esp_ota_get_running_partition()->label); // welcome message
#else
LOG_INFO("Starting EMS-ESP version %s", EMSESP_APP_VERSION); // welcome message LOG_INFO("Starting EMS-ESP version %s", EMSESP_APP_VERSION); // welcome message
#endif
LOG_DEBUG("System is running in Debug mode"); LOG_DEBUG("System is running in Debug mode");
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str()); LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());

View File

@@ -65,6 +65,7 @@ MAKE_WORD(users)
MAKE_WORD(publish) MAKE_WORD(publish)
MAKE_WORD(board_profile) MAKE_WORD(board_profile)
MAKE_WORD(setvalue) MAKE_WORD(setvalue)
MAKE_WORD(service)
// for commands // for commands
MAKE_WORD(call) MAKE_WORD(call)
@@ -142,6 +143,7 @@ MAKE_WORD_CUSTOM(asterisks, "********")
MAKE_WORD_CUSTOM(n_mandatory, "<n>") MAKE_WORD_CUSTOM(n_mandatory, "<n>")
MAKE_WORD_CUSTOM(sensorid_optional, "[sensor ID]") MAKE_WORD_CUSTOM(sensorid_optional, "[sensor ID]")
MAKE_WORD_CUSTOM(id_optional, "[id|hc]") MAKE_WORD_CUSTOM(id_optional, "[id|hc]")
MAKE_WORD_CUSTOM(partitionname_optional, "[partitionsname]")
MAKE_WORD_CUSTOM(data_optional, "[data]") MAKE_WORD_CUSTOM(data_optional, "[data]")
MAKE_WORD_CUSTOM(nvs_optional, "[nvs]") MAKE_WORD_CUSTOM(nvs_optional, "[nvs]")
MAKE_WORD_CUSTOM(offset_optional, "[offset]") MAKE_WORD_CUSTOM(offset_optional, "[offset]")
@@ -157,6 +159,8 @@ MAKE_WORD_CUSTOM(new_password_prompt1, "Enter new password: ")
MAKE_WORD_CUSTOM(new_password_prompt2, "Retype new password: ") MAKE_WORD_CUSTOM(new_password_prompt2, "Retype new password: ")
MAKE_WORD_CUSTOM(password_prompt, "Password: ") MAKE_WORD_CUSTOM(password_prompt, "Password: ")
MAKE_WORD_CUSTOM(unset, "<unset>") MAKE_WORD_CUSTOM(unset, "<unset>")
MAKE_WORD_CUSTOM(enable_mandatory, "<enable | disable>")
MAKE_WORD_CUSTOM(service_mandatory, "<ota | ap | mqtt | ntp>")
// more common names that don't need translations // more common names that don't need translations
MAKE_NOTRANSLATION(1x3min, "1x3min") MAKE_NOTRANSLATION(1x3min, "1x3min")
@@ -327,7 +331,7 @@ MAKE_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 value
MAKE_ENUM(enum_nofrostmode, FL_(off), FL_(outdoor), FL_(room)) MAKE_ENUM(enum_nofrostmode, FL_(off), FL_(outdoor), FL_(room))
MAKE_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor)) MAKE_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor))
MAKE_ENUM(enum_controlmode, FL_(off), FL_(optimized), FL_(simple), FL_(mpc), FL_(room), FL_(power), FL_(constant)) MAKE_ENUM(enum_controlmode, FL_(optimized), FL_(simple), FL_(na), FL_(room), FL_(power))
MAKE_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoint), FL_(na), FL_(room), FL_(power), FL_(constant)) // RC310 1-4 MAKE_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoint), FL_(na), FL_(room), FL_(power), FL_(constant)) // RC310 1-4
MAKE_ENUM(enum_controlmode2, FL_(outdoor), FL_(room)) MAKE_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
MAKE_ENUM(enum_controlmode3, FL_(off), FL_(unmixed), FL_(unmixedIPM), FL_(mixed)) MAKE_ENUM(enum_controlmode3, FL_(off), FL_(unmixed), FL_(unmixedIPM), FL_(mixed))

View File

@@ -264,12 +264,40 @@ void System::store_nvs_values() {
} }
// restart EMS-ESP // restart EMS-ESP
void System::system_restart() { void System::system_restart(const char * partitionname) {
#ifndef EMSESP_STANDALONE
if (partitionname != nullptr) {
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
if (partition && strcmp(partition->label, partitionname) == 0) {
esp_ota_set_boot_partition(partition);
} else if (strcmp(esp_ota_get_running_partition()->label, partitionname) != 0) {
partition = esp_ota_get_next_update_partition(NULL);
if (!partition) {
LOG_ERROR("Partition '%s' not found", partitionname);
return;
}
if (strcmp(partition->label, partitionname) != 0 && strcmp(partitionname, "boot") != 0) {
partition = esp_ota_get_next_update_partition(partition);
if (!partition || strcmp(partition->label, partitionname)) {
LOG_ERROR("Partition '%s' not found", partitionname);
return;
}
}
uint64_t buffer;
esp_partition_read(partition, 0, &buffer, 8);
if (buffer == 0xFFFFFFFFFFFFFFFF) { // partition empty
LOG_ERROR("Partition '%s' is empty, not bootable", partition->label);
return;
}
esp_ota_set_boot_partition(partition);
}
LOG_INFO("Restarting EMS-ESP from partition '%s'", partitionname);
} else {
LOG_INFO("Restarting EMS-ESP..."); LOG_INFO("Restarting EMS-ESP...");
}
store_nvs_values(); store_nvs_values();
Shell::loop_all(); Shell::loop_all();
delay(1000); // wait a second delay(1000); // wait a second
#ifndef EMSESP_STANDALONE
ESP.restart(); ESP.restart();
#endif #endif
} }
@@ -462,7 +490,8 @@ void System::button_OnDblClick(PButton & b) {
// button long press // button long press
void System::button_OnLongPress(PButton & b) { void System::button_OnLongPress(PButton & b) {
LOG_NOTICE("Button pressed - long press"); LOG_NOTICE("Button pressed - long press - restart other partition");
EMSESP::system_.system_restart("boot");
} }
// button indefinite press // button indefinite press
@@ -1517,9 +1546,13 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
return true; return true;
} }
// restart command - perform a hard reset by setting flag // restart command - perform a hard reset
bool System::command_restart(const char * value, const int8_t id) { bool System::command_restart(const char * value, const int8_t id) {
restart_requested(true); if (value != nullptr && value[0] == '\0') {
EMSESP::system_.system_restart(value);
} else {
EMSESP::system_.system_restart();
}
return true; return true;
} }

View File

@@ -69,7 +69,7 @@ class System {
std::string reset_reason(uint8_t cpu) const; std::string reset_reason(uint8_t cpu) const;
void store_nvs_values(); void store_nvs_values();
void system_restart(); void system_restart(const char * partition = nullptr);
void format(uuid::console::Shell & shell); void format(uuid::console::Shell & shell);
void upload_status(bool in_progress); void upload_status(bool in_progress);
bool upload_status(); bool upload_status();

View File

@@ -301,7 +301,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "add") { if (command == "add") {
shell.printfln("Testing Adding a device (product_id %d), with all values...", id2); shell.printfln("Testing Adding a device (product_id %d), with all values...", id2);
test("add", id1, id2); // e.g. 8 172 test("add", id1, id2); // e.g. "test add 0x8 172"
shell.invoke_command("show values"); shell.invoke_command("show values");
ok = true; ok = true;
} }
@@ -342,13 +342,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
AsyncWebServerRequest request; AsyncWebServerRequest request;
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/custom"); request.url("/api/custom");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/custom/test_custom"); request.url("/api/custom/test_custom");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/custom/test_read_only"); request.url("/api/custom/test_read_only");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/custom/test_ram"); request.url("/api/custom/test_ram");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
shell.invoke_command("call custom info"); shell.invoke_command("call custom info");
#endif #endif
@@ -365,7 +365,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
AsyncWebServerRequest request; AsyncWebServerRequest request;
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/scheduler"); request.url("/api/scheduler");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
shell.invoke_command("call scheduler info"); shell.invoke_command("call scheduler info");
#endif #endif
ok = true; ok = true;
@@ -379,7 +379,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
AsyncWebServerRequest request; AsyncWebServerRequest request;
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/boiler/coldshot"); request.url("/api/boiler/coldshot");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
#else #else
shell.invoke_command("call boiler coldshot"); shell.invoke_command("call boiler coldshot");
#endif #endif
@@ -777,11 +777,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
AsyncWebServerRequest request; AsyncWebServerRequest request;
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/temperaturesensor/commands"); request.url("/api/temperaturesensor/commands");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/temperaturesensor/info"); request.url("/api/temperaturesensor/info");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/temperaturesensor/01-0203-0405-0607"); request.url("/api/temperaturesensor/01-0203-0405-0607");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
ok = true; ok = true;
} }
@@ -827,12 +827,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
AsyncWebServerRequest request; AsyncWebServerRequest request;
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/analogsensor/commands"); request.url("/api/analogsensor/commands");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/analogsensor/info"); request.url("/api/analogsensor/info");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/analogsensor/test_analog1"); request.url("/api/analogsensor/test_analog1");
request.url("/api/analogsensor/36"); request.url("/api/analogsensor/36");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
// test renaming it // test renaming it
// bool update(uint8_t id, const std::string & name, int16_t offset, float factor, uint8_t uom, uint8_t type); // bool update(uint8_t id, const std::string & name, int16_t offset, float factor, uint8_t uom, uint8_t type);
@@ -948,19 +948,19 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/boiler/values"); request.url("/api/boiler/values");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/wwcirc"); request.url("/api/boiler/wwcirc");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/wwcirc/fullname"); request.url("/api/boiler/wwcirc/fullname");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/selburnpow/value"); request.url("/api/boiler/selburnpow/value");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/wwchargetype/writeable"); request.url("/api/boiler/wwchargetype/writeable");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/flamecurr/value"); request.url("/api/boiler/flamecurr/value");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/flamecurr/bad"); request.url("/api/boiler/flamecurr/bad");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
ok = true; ok = true;
} }
@@ -995,7 +995,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(doc, odata); deserializeJson(doc, odata);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api/thermostat/wwmode"); request.url("/api/thermostat/wwmode");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
ok = true; ok = true;
} }
@@ -1019,76 +1019,76 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
/* /*
requestX.url("/api"); // should fail requestX.url("/api"); // should fail
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
*/ */
/* /*
requestX.url("/api/thermostat/seltemp"); requestX.url("/api/thermostat/seltemp");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
*/ */
/* /*
requestX.url("/api/thermostat/mode/auto"); requestX.url("/api/thermostat/mode/auto");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
*/ */
/* /*
requestX.url("/api/thermostat"); // check if defaults to info requestX.url("/api/thermostat"); // check if defaults to info
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
requestX.url("/api/thermostat/info"); requestX.url("/api/thermostat/info");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
requestX.url("/api/thermostat/values"); requestX.url("/api/thermostat/values");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
requestX.url("/api/thermostat/mode"); requestX.url("/api/thermostat/mode");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
*/ */
/* /*
requestX.url("/api/system"); // check if defaults to info requestX.url("/api/system"); // check if defaults to info
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
emsesp::EMSESP::logger().notice("*"); emsesp::EMSESP::logger().notice("*");
requestX.url("/api/system/info"); requestX.url("/api/system/info");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
emsesp::EMSESP::logger().notice("*"); emsesp::EMSESP::logger().notice("*");
requestX.url("/api/thermostat"); // check if defaults to values requestX.url("/api/thermostat"); // check if defaults to values
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
emsesp::EMSESP::logger().notice("*"); emsesp::EMSESP::logger().notice("*");
requestX.url("/api/thermostat/info"); requestX.url("/api/thermostat/info");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
emsesp::EMSESP::logger().notice("*"); emsesp::EMSESP::logger().notice("*");
requestX.url("/api/thermostat/seltemp"); requestX.url("/api/thermostat/seltemp");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
*/ */
/* /*
requestX.url("/api/system/restart"); requestX.url("/api/system/restart");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
*/ */
/* /*
requestX.url("/api/temperaturesensor/xxxx"); requestX.url("/api/temperaturesensor/xxxx");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
emsesp::EMSESP::logger().notice("****"); emsesp::EMSESP::logger().notice("****");
requestX.url("/api/temperaturesensor/info"); requestX.url("/api/temperaturesensor/info");
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
return; return;
*/ */
/* /*
requestX.url("/api"); // should fail requestX.url("/api"); // should fail
EMSESP::webAPIService.webAPIService_get(&requestX); EMSESP::webAPIService.webAPIService(&requestX);
*/ */
requestX.method(HTTP_POST); requestX.method(HTTP_POST);
@@ -1098,7 +1098,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(docX, dataX); deserializeJson(docX, dataX);
jsonX = docX.as<JsonVariant>(); jsonX = docX.as<JsonVariant>();
requestX.url("/api"); requestX.url("/api");
EMSESP::webAPIService.webAPIService_post(&requestX, jsonX); EMSESP::webAPIService.webAPIService(&requestX, jsonX);
return; return;
*/ */
@@ -1109,7 +1109,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
jsonX = docX.as<JsonVariant>(); jsonX = docX.as<JsonVariant>();
// requestX.url("/api/system/send"); // requestX.url("/api/system/send");
requestX.url("/api/thermostat"); requestX.url("/api/thermostat");
EMSESP::webAPIService.webAPIService_post(&requestX, jsonX); EMSESP::webAPIService.webAPIService(&requestX, jsonX);
return; return;
*/ */
@@ -1118,7 +1118,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(docX, dataX); deserializeJson(docX, dataX);
jsonX = docX.as<JsonVariant>(); jsonX = docX.as<JsonVariant>();
requestX.url("/api/thermostat/mode/auto"); // should fail requestX.url("/api/thermostat/mode/auto"); // should fail
EMSESP::webAPIService.webAPIService_post(&requestX, jsonX); EMSESP::webAPIService.webAPIService(&requestX, jsonX);
return; return;
*/ */
@@ -1185,21 +1185,21 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/thermostat"); // check if defaults to info request.url("/api/thermostat"); // check if defaults to info
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/thermostat/info"); request.url("/api/thermostat/info");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/thermostat/values"); request.url("/api/thermostat/values");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/thermostat/seltemp"); request.url("/api/thermostat/seltemp");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/system/commands"); request.url("/api/system/commands");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/system/info"); request.url("/api/system/info");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/syspress"); request.url("/api/boiler/syspress");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/boiler/wwcurflow"); request.url("/api/boiler/wwcurflow");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
// POST tests // POST tests
request.method(HTTP_POST); request.method(HTTP_POST);
@@ -1211,28 +1211,28 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(doc, data1); deserializeJson(doc, data1);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api/thermostat"); request.url("/api/thermostat");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
// 2 // 2
char data2[] = "{\"value\":12}"; char data2[] = "{\"value\":12}";
deserializeJson(doc, data2); deserializeJson(doc, data2);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api/thermostat/seltemp"); request.url("/api/thermostat/seltemp");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
// 3 // 3
char data3[] = "{\"device\":\"thermostat\", \"cmd\":\"seltemp\",\"value\":13}"; char data3[] = "{\"device\":\"thermostat\", \"cmd\":\"seltemp\",\"value\":13}";
deserializeJson(doc, data3); deserializeJson(doc, data3);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api"); request.url("/api");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
// 4 - system call // 4 - system call
char data4[] = "{\"value\":\"0B 88 19 19 02\"}"; char data4[] = "{\"value\":\"0B 88 19 19 02\"}";
deserializeJson(doc, data4); deserializeJson(doc, data4);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api/system/send"); request.url("/api/system/send");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
// 5 - test write value // 5 - test write value
// device=3 cmd=hc2/seltemp value=44 // device=3 cmd=hc2/seltemp value=44
@@ -1240,7 +1240,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(doc, data5); deserializeJson(doc, data5);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api"); request.url("/api");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
// write value from web - testing hc2/seltemp // write value from web - testing hc2/seltemp
char data6[] = "{\"id\":2,\"devicevalue\":{\"v\":\"44\",\"u\":1,\"n\":\"hc2 selected room temperature\",\"c\":\"hc2/seltemp\"}"; char data6[] = "{\"id\":2,\"devicevalue\":{\"v\":\"44\",\"u\":1,\"n\":\"hc2 selected room temperature\",\"c\":\"hc2/seltemp\"}";
@@ -1254,7 +1254,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(doc, data7); deserializeJson(doc, data7);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api"); request.url("/api");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
emsesp::EMSESP::logger().warning("* these next ones should fail *"); emsesp::EMSESP::logger().warning("* these next ones should fail *");
@@ -1270,7 +1270,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(doc, data9); deserializeJson(doc, data9);
json = doc.as<JsonVariant>(); json = doc.as<JsonVariant>();
request.url("/api/thermostat/mode/auto"); request.url("/api/thermostat/mode/auto");
EMSESP::webAPIService.webAPIService_post(&request, json); EMSESP::webAPIService.webAPIService(&request, json);
ok = true; ok = true;
} }
@@ -1811,11 +1811,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// test API // test API
AsyncWebServerRequest request; AsyncWebServerRequest request;
request.url("/api/mixer"); request.url("/api/mixer");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/mixer/hc1/pumpstatus"); request.url("/api/mixer/hc1/pumpstatus");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
request.url("/api/mixer/wwc2/pumpstatus"); request.url("/api/mixer/wwc2/pumpstatus");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService(&request);
ok = true; ok = true;
} }

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.5-test.16a" #define EMSESP_APP_VERSION "3.6.5-test.18"

View File

@@ -24,12 +24,11 @@ uint32_t WebAPIService::api_count_ = 0;
uint16_t WebAPIService::api_fails_ = 0; uint16_t WebAPIService::api_fails_ = 0;
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager) WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
: _securityManager(securityManager) : _securityManager(securityManager) {
, _apiHandler(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService_post(request, json); }) { // for POSTs // API
server->on(EMSESP_API_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { webAPIService_get(request); }); // for GETs server->on(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService(request, json); });
server->addHandler(&_apiHandler);
// for settings // settings
server->on(GET_SETTINGS_PATH, server->on(GET_SETTINGS_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getSettings(request); }, AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getSettings(request); }, AuthenticationPredicates::IS_ADMIN));
@@ -47,31 +46,30 @@ WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * security
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getEntities(request); }, AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getEntities(request); }, AuthenticationPredicates::IS_ADMIN));
} }
// HTTP GET // POST|GET /{device}
// GET /{device} // POST|GET /{device}/{entity}
// GET /{device}/{entity} void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant json) {
void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) { JsonObject input;
// has no body JSON so create dummy as empty input object
JsonDocument input_doc;
JsonObject input = input_doc.to<JsonObject>();
parse(request, input);
}
// For HTTP POSTS with an optional JSON body
// HTTP_POST | HTTP_PUT | HTTP_PATCH
// POST /{device}[/{hc|id}][/{name}]
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant json) {
// if no body then treat it as a secure GET // if no body then treat it as a secure GET
if (!json.is<JsonObject>()) { if ((request->method() == HTTP_GET) || (!json.is<JsonObject>())) {
webAPIService_get(request); // HTTP GET
return; JsonDocument input_doc; // has no body JSON so create dummy as empty input object
input = input_doc.to<JsonObject>();
} else {
// HTTP_POST | HTTP_PUT | HTTP_PATCH
input = json.as<JsonObject>(); // extract values from the json. these will be used as default values
} }
// extract values from the json. these will be used as default values
auto && input = json.as<JsonObject>();
parse(request, input); parse(request, input);
} }
#ifdef EMSESP_TEST
// for test.cpp so we can invoke GETs to test the API
void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
JsonDocument input_doc;
parse(request, input_doc.to<JsonObject>());
}
#endif
// parse the URL looking for query or path parameters // parse the URL looking for query or path parameters
// reporting back any errors // reporting back any errors
void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
@@ -84,7 +82,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
// check for query parameters first, the old style from v2 // check for query parameters first, the old style from v2
// api?device={device}&cmd={name}&data={value}&id={hc} // api?device={device}&cmd={name}&data={value}&id={hc}
if (request->url() == "/api") { if (request->url() == EMSESP_API_SERVICE_PATH) {
// get the device // get the device
if (request->hasParam(F_(device))) { if (request->hasParam(F_(device))) {
input["device"] = request->getParam(F_(device))->value().c_str(); input["device"] = request->getParam(F_(device))->value().c_str();

View File

@@ -20,6 +20,7 @@
#define WebAPIService_h #define WebAPIService_h
#define EMSESP_API_SERVICE_PATH "/api" #define EMSESP_API_SERVICE_PATH "/api"
#define GET_SETTINGS_PATH "/rest/getSettings" #define GET_SETTINGS_PATH "/rest/getSettings"
#define GET_CUSTOMIZATIONS_PATH "/rest/getCustomizations" #define GET_CUSTOMIZATIONS_PATH "/rest/getCustomizations"
#define GET_SCHEDULE_PATH "/rest/getSchedule" #define GET_SCHEDULE_PATH "/rest/getSchedule"
@@ -31,8 +32,12 @@ class WebAPIService {
public: public:
WebAPIService(AsyncWebServer * server, SecurityManager * securityManager); WebAPIService(AsyncWebServer * server, SecurityManager * securityManager);
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant json); // for POSTs void webAPIService(AsyncWebServerRequest * request, JsonVariant json);
void webAPIService_get(AsyncWebServerRequest * request); // for GETs
#ifdef EMSESP_TEST
// for test.cpp
void webAPIService(AsyncWebServerRequest * request);
#endif
static uint32_t api_count() { static uint32_t api_count() {
return api_count_; return api_count_;
@@ -44,13 +49,11 @@ class WebAPIService {
private: private:
SecurityManager * _securityManager; SecurityManager * _securityManager;
AsyncCallbackJsonWebHandler _apiHandler; // for POSTs
static uint32_t api_count_; static uint32_t api_count_;
static uint16_t api_fails_; static uint16_t api_fails_;
void parse(AsyncWebServerRequest * request, JsonObject input); void parse(AsyncWebServerRequest * request, JsonObject input);
void getSettings(AsyncWebServerRequest * request); void getSettings(AsyncWebServerRequest * request);
void getCustomizations(AsyncWebServerRequest * request); void getCustomizations(AsyncWebServerRequest * request);
void getSchedule(AsyncWebServerRequest * request); void getSchedule(AsyncWebServerRequest * request);

View File

@@ -23,10 +23,7 @@ namespace emsesp {
bool WebCustomization::_start = true; bool WebCustomization::_start = true;
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE) : _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE) {
, _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH,
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { customization_entities(request, json); },
AuthenticationPredicates::IS_AUTHENTICATED)) {
server->on(DEVICE_ENTITIES_PATH, server->on(DEVICE_ENTITIES_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { device_entities(request); }, AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { device_entities(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
@@ -39,9 +36,9 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f
HTTP_POST, HTTP_POST,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { reset_customization(request); }, AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { reset_customization(request); }, AuthenticationPredicates::IS_ADMIN));
_masked_entities_handler.setMethod(HTTP_POST); server->on(CUSTOMIZATION_ENTITIES_PATH,
_masked_entities_handler.setMaxContentLength(2048); securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { customization_entities(request, json); },
server->addHandler(&_masked_entities_handler); AuthenticationPredicates::IS_AUTHENTICATED));
} }
// this creates the customization file, saving it to the FS // this creates the customization file, saving it to the FS

View File

@@ -101,8 +101,6 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
// POST // POST
void customization_entities(AsyncWebServerRequest * request, JsonVariant json); void customization_entities(AsyncWebServerRequest * request, JsonVariant json);
void reset_customization(AsyncWebServerRequest * request); // command void reset_customization(AsyncWebServerRequest * request); // command
AsyncCallbackJsonWebHandler _masked_entities_handler;
}; };
} // namespace emsesp } // namespace emsesp

View File

@@ -21,16 +21,18 @@
namespace emsesp { namespace emsesp {
WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager) WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager)
: _write_value_handler(WRITE_DEVICE_VALUE_SERVICE_PATH,
{
// write endpoints
server->on(WRITE_DEVICE_VALUE_SERVICE_PATH,
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_device_value(request, json); }, securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_device_value(request, json); },
AuthenticationPredicates::IS_ADMIN)) AuthenticationPredicates::IS_ADMIN));
, _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH, server->on(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH,
securityManager->wrapCallback([this](AsyncWebServerRequest * request, securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_temperature_sensor(request, json); },
JsonVariant json) { write_temperature_sensor(request, json); }, AuthenticationPredicates::IS_ADMIN));
AuthenticationPredicates::IS_ADMIN)) server->on(WRITE_ANALOG_SENSOR_SERVICE_PATH,
, _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH,
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_analog_sensor(request, json); }, securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_analog_sensor(request, json); },
AuthenticationPredicates::IS_ADMIN)) { AuthenticationPredicates::IS_ADMIN));
// GET's // GET's
server->on(DEVICE_DATA_SERVICE_PATH, server->on(DEVICE_DATA_SERVICE_PATH,
HTTP_GET, HTTP_GET,
@@ -49,19 +51,6 @@ WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securi
server->on(SCAN_DEVICES_SERVICE_PATH, server->on(SCAN_DEVICES_SERVICE_PATH,
HTTP_POST, HTTP_POST,
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { scan_devices(request); }, AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest([this](AsyncWebServerRequest * request) { scan_devices(request); }, AuthenticationPredicates::IS_ADMIN));
_write_value_handler.setMethod(HTTP_POST);
_write_value_handler.setMaxContentLength(256);
server->addHandler(&_write_value_handler);
_write_temperature_handler.setMethod(HTTP_POST);
_write_temperature_handler.setMaxContentLength(256);
server->addHandler(&_write_temperature_handler);
_write_analog_handler.setMethod(HTTP_POST);
_write_analog_handler.setMaxContentLength(256);
server->addHandler(&_write_analog_handler);
} }
// scan devices service // scan devices service

View File

@@ -51,8 +51,6 @@ class WebDataService {
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json); void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json);
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json); void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json);
void scan_devices(AsyncWebServerRequest * request); // command void scan_devices(AsyncWebServerRequest * request); // command
AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler;
}; };
} // namespace emsesp } // namespace emsesp

View File

@@ -21,17 +21,14 @@
namespace emsesp { namespace emsesp {
WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager) WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager)
: events_(EVENT_SOURCE_LOG_PATH) : events_(EVENT_SOURCE_LOG_PATH) {
, setValues_(LOG_SETTINGS_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { setValues(request, json); }) { // get & set settings
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); server->on(LOG_SETTINGS_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { getSetValues(request, json); });
// get settings
server->on(LOG_SETTINGS_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { getValues(request); });
// for bring back the whole log - is a command, hence a POST // for bring back the whole log - is a command, hence a POST
server->on(FETCH_LOG_PATH, HTTP_POST, [this](AsyncWebServerRequest * request) { fetchLog(request); }); server->on(FETCH_LOG_PATH, HTTP_POST, [this](AsyncWebServerRequest * request) { fetchLog(request); });
server->addHandler(&setValues_); events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
server->addHandler(&events_); server->addHandler(&events_);
} }
@@ -204,11 +201,20 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) {
} }
// sets the values like level after a POST // sets the values like level after a POST
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant json) { void WebLogService::getSetValues(AsyncWebServerRequest * request, JsonVariant json) {
if (!json.is<JsonObject>()) { if ((request->method() == HTTP_GET) || (!json.is<JsonObject>())) {
// GET - return the values
auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot();
root["level"] = log_level();
root["max_messages"] = maximum_log_messages();
root["compact"] = compact();
response->setLength();
request->send(response);
return; return;
} }
// POST - write the settings
auto && body = json.as<JsonObject>(); auto && body = json.as<JsonObject>();
uuid::log::Level level = body["level"]; uuid::log::Level level = body["level"];
@@ -223,15 +229,4 @@ void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant json)
request->send(200); // OK request->send(200); // OK
} }
// return the current value settings after a GET
void WebLogService::getValues(AsyncWebServerRequest * request) {
auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot();
root["level"] = log_level();
root["max_messages"] = maximum_log_messages();
root["compact"] = compact();
response->setLength();
request->send(response);
}
} // namespace emsesp } // namespace emsesp

View File

@@ -60,14 +60,10 @@ class WebLogService : public uuid::log::Handler {
void transmit(const QueuedLogMessage & message); void transmit(const QueuedLogMessage & message);
void fetchLog(AsyncWebServerRequest * request); void fetchLog(AsyncWebServerRequest * request);
void getValues(AsyncWebServerRequest * request); void getSetValues(AsyncWebServerRequest * request, JsonVariant json);
char * messagetime(char * out, const uint64_t t, const size_t bufsize); char * messagetime(char * out, const uint64_t t, const size_t bufsize);
void setValues(AsyncWebServerRequest * request, JsonVariant json);
AsyncCallbackJsonWebHandler setValues_; // for POSTs
uint64_t last_transmit_ = 0; // Last transmit time uint64_t last_transmit_ = 0; // Last transmit time
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; // Maximum number of log messages to buffer before they are output size_t maximum_log_messages_ = MAX_LOG_MESSAGES; // Maximum number of log messages to buffer before they are output
size_t limit_log_messages_ = 1; // dynamic limit size_t limit_log_messages_ = 1; // dynamic limit