mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
Merge branch 'dev' into dev2
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,8 +1,5 @@
|
||||
# vscode
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/extensions.json
|
||||
.vscode/launch.json
|
||||
.vscode/settings.json
|
||||
.vscode/*
|
||||
|
||||
# c++ compiling
|
||||
.clang_complete
|
||||
@@ -60,3 +57,4 @@ bw-output/
|
||||
|
||||
# testing
|
||||
emsesp
|
||||
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -2,8 +2,6 @@
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
|
||||
88
.vscode/settings.json
vendored
88
.vscode/settings.json
vendored
@@ -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
18
.vscode/tasks.json
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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)
|
||||
- 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)
|
||||
- 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
|
||||
|
||||
@@ -42,6 +46,7 @@
|
||||
- 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)
|
||||
- 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
|
||||
|
||||
@@ -52,4 +57,5 @@
|
||||
- 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>
|
||||
- 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
|
||||
|
||||
9
esp32_partition_16M_factory.csv
Normal file
9
esp32_partition_16M_factory.csv
Normal 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,
|
||||
|
File diff suppressed because one or more lines are too long
@@ -4,4 +4,4 @@ enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.1.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.1.1.cjs
|
||||
|
||||
@@ -26,14 +26,14 @@
|
||||
"@babel/core": "^7.24.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.15.12",
|
||||
"@mui/material": "^5.15.12",
|
||||
"@mui/icons-material": "^5.15.13",
|
||||
"@mui/material": "^5.15.13",
|
||||
"@table-library/react-table-library": "4.1.7",
|
||||
"@types/imagemin": "^8.0.5",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.11.25",
|
||||
"@types/react": "^18.2.64",
|
||||
"@types/react-dom": "^18.2.21",
|
||||
"@types/node": "^20.11.28",
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"alova": "^2.17.1",
|
||||
"async-validator": "^4.2.5",
|
||||
@@ -45,17 +45,17 @@
|
||||
"react-dom": "latest",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-router-dom": "^6.22.2",
|
||||
"react-toastify": "^10.0.4",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-toastify": "^10.0.5",
|
||||
"sockette": "^2.0.6",
|
||||
"typesafe-i18n": "^5.26.2",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/compat": "^17.1.2",
|
||||
"@preact/preset-vite": "^2.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
"@preact/preset-vite": "^2.8.2",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
@@ -69,10 +69,10 @@
|
||||
"preact": "^10.19.6",
|
||||
"prettier": "^3.2.5",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"terser": "^5.29.1",
|
||||
"vite": "^5.1.5",
|
||||
"terser": "^5.29.2",
|
||||
"vite": "^5.1.6",
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const bytesPerLine = 20;
|
||||
var totalSize = 0;
|
||||
|
||||
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
|
||||
|
||||
class WWWData {
|
||||
|
||||
2452
interface/yarn.lock
2452
interface/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,7 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
|
||||
|
||||
~AsyncJsonResponse() {
|
||||
}
|
||||
JsonVariant & getRoot() {
|
||||
JsonVariant getRoot() {
|
||||
return _root;
|
||||
}
|
||||
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 {
|
||||
private:
|
||||
@@ -142,13 +142,15 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
||||
if (!_onRequest)
|
||||
return false;
|
||||
|
||||
if (!(_method & request->method()))
|
||||
WebRequestMethodComposite request_method = request->method();
|
||||
|
||||
if (!(_method & request_method))
|
||||
return false;
|
||||
|
||||
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
||||
return false;
|
||||
|
||||
if (!request->contentType().equalsIgnoreCase(JSON_MIMETYPE))
|
||||
if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(JSON_MIMETYPE))
|
||||
return false;
|
||||
|
||||
request->addInterestingHeader("ANY");
|
||||
@@ -156,18 +158,23 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
||||
}
|
||||
|
||||
virtual void handleRequest(AsyncWebServerRequest * request) override final {
|
||||
if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||
return request->requestAuthentication();
|
||||
if (_onRequest) {
|
||||
JsonVariant json; // empty variant
|
||||
if (request->_tempObject != NULL) {
|
||||
if (request->method() == HTTP_GET) {
|
||||
JsonVariant json;
|
||||
_onRequest(request, json);
|
||||
return;
|
||||
} else if (request->_tempObject != NULL) {
|
||||
JsonDocument jsonBuffer;
|
||||
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject));
|
||||
if (!error) {
|
||||
json = jsonBuffer.as<JsonVariant>();
|
||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||
_onRequest(request, json);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_onRequest(request, json);
|
||||
request->send(_contentLength > _maxContentLength ? 413 : 400);
|
||||
} else {
|
||||
request->send(500);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <vector>
|
||||
#include "FS.h"
|
||||
|
||||
#include <ArduinoJson.h> // added by proddy
|
||||
|
||||
#include "StringArray.h"
|
||||
|
||||
#ifdef ESP32
|
||||
@@ -84,7 +86,7 @@ namespace fs {
|
||||
static const char * write;
|
||||
static const char * append;
|
||||
};
|
||||
};
|
||||
}; // namespace fs
|
||||
#else
|
||||
#include "FileOpenMode.h"
|
||||
#endif
|
||||
@@ -108,13 +110,28 @@ class AsyncWebParameter {
|
||||
bool _isFile;
|
||||
|
||||
public:
|
||||
|
||||
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){}
|
||||
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; }
|
||||
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) {
|
||||
}
|
||||
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(const AsyncWebHeader &) = default;
|
||||
|
||||
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
|
||||
AsyncWebHeader(const String& data): _name(), _value(){
|
||||
if(!data) return;
|
||||
AsyncWebHeader(const String & name, const String & value)
|
||||
: _name(name)
|
||||
, _value(value) {
|
||||
}
|
||||
AsyncWebHeader(const String & data)
|
||||
: _name()
|
||||
, _value() {
|
||||
if (!data)
|
||||
return;
|
||||
int index = data.indexOf(':');
|
||||
if (index < 0) return;
|
||||
if (index < 0)
|
||||
return;
|
||||
_name = data.substring(0, index);
|
||||
_value = data.substring(index + 2);
|
||||
}
|
||||
|
||||
AsyncWebHeader & operator=(const AsyncWebHeader &) = default;
|
||||
|
||||
const String& name() const { return _name; }
|
||||
const String& value() const { return _value; }
|
||||
String toString() const { return _name + F(": ") + _value + F("\r\n"); }
|
||||
const String & name() const {
|
||||
return _name;
|
||||
}
|
||||
const String & value() const {
|
||||
return _value;
|
||||
}
|
||||
String toString() const {
|
||||
return _name + F(": ") + _value + F("\r\n");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -160,6 +190,7 @@ class AsyncWebServerRequest {
|
||||
using FS = fs::FS;
|
||||
friend class AsyncWebServer;
|
||||
friend class AsyncCallbackWebHandler;
|
||||
|
||||
private:
|
||||
AsyncClient * _client;
|
||||
AsyncWebServer * _server;
|
||||
@@ -231,17 +262,35 @@ class AsyncWebServerRequest {
|
||||
AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
|
||||
~AsyncWebServerRequest();
|
||||
|
||||
AsyncClient* client(){ return _client; }
|
||||
uint8_t version() const { return _version; }
|
||||
WebRequestMethodComposite method() const { return _method; }
|
||||
const String& url() const { 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; }
|
||||
AsyncClient * client() {
|
||||
return _client;
|
||||
}
|
||||
uint8_t version() const {
|
||||
return _version;
|
||||
}
|
||||
WebRequestMethodComposite method() const {
|
||||
return _method;
|
||||
}
|
||||
const String & url() const {
|
||||
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; }
|
||||
RequestedConnectionType requestedConnType() const {
|
||||
return _reqconntype;
|
||||
}
|
||||
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
|
||||
void onDisconnect(ArDisconnectHandler fn);
|
||||
|
||||
@@ -252,7 +301,9 @@ class AsyncWebServerRequest {
|
||||
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 setHandler(AsyncWebHandler *handler){ _handler = handler; }
|
||||
void setHandler(AsyncWebHandler * handler) {
|
||||
_handler = handler;
|
||||
}
|
||||
void addInterestingHeader(const String & name);
|
||||
|
||||
void redirect(const String & url);
|
||||
@@ -268,8 +319,10 @@ class AsyncWebServerRequest {
|
||||
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(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
||||
AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
||||
AsyncWebServerResponse *
|
||||
beginResponse(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
|
||||
AsyncWebServerResponse *
|
||||
beginResponse(File content, 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 * beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||
AsyncWebServerResponse * beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||
@@ -277,6 +330,9 @@ class AsyncWebServerRequest {
|
||||
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
|
||||
bool hasHeader(const String & name) const; // check if header exists
|
||||
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
|
||||
@@ -296,7 +352,9 @@ class AsyncWebServerRequest {
|
||||
AsyncWebParameter * getParam(const __FlashStringHelper * data, bool post, bool file) const;
|
||||
AsyncWebParameter * getParam(size_t num) const;
|
||||
|
||||
size_t args() const { return params(); } // get arguments count
|
||||
size_t args() const {
|
||||
return params();
|
||||
} // get arguments count
|
||||
const String & arg(const String & name) const; // get request argument value by name
|
||||
const String & arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
|
||||
const String & arg(size_t i) const; // get request argument value by number
|
||||
@@ -333,21 +391,40 @@ class AsyncWebRewrite {
|
||||
String _toUrl;
|
||||
String _params;
|
||||
ArRequestFilterFunction _filter;
|
||||
|
||||
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('?');
|
||||
if (index > 0) {
|
||||
_params = _toUrl.substring(index + 1);
|
||||
_toUrl = _toUrl.substring(0, index);
|
||||
}
|
||||
}
|
||||
virtual ~AsyncWebRewrite(){}
|
||||
AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
|
||||
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); }
|
||||
virtual ~AsyncWebRewrite() {
|
||||
}
|
||||
AsyncWebRewrite & setFilter(ArRequestFilterFunction fn) {
|
||||
_filter = fn;
|
||||
return *this;
|
||||
}
|
||||
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;
|
||||
String _username;
|
||||
String _password;
|
||||
|
||||
public:
|
||||
AsyncWebHandler():_username(""), _password(""){}
|
||||
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _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(){}
|
||||
AsyncWebHandler()
|
||||
: _username("")
|
||||
, _password("") {
|
||||
}
|
||||
AsyncWebHandler & setFilter(ArRequestFilterFunction fn) {
|
||||
_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;
|
||||
}
|
||||
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 bool isRequestHandlerTrivial(){return true;}
|
||||
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 bool isRequestHandlerTrivial() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* RESPONSE :: One instance is created for each Request (attached by the Handler)
|
||||
* */
|
||||
|
||||
typedef enum {
|
||||
RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
|
||||
} WebResponseState;
|
||||
typedef enum { RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState;
|
||||
|
||||
class AsyncWebServerResponse {
|
||||
protected:
|
||||
@@ -396,6 +499,7 @@ class AsyncWebServerResponse {
|
||||
size_t _writtenLength;
|
||||
WebResponseState _state;
|
||||
const char * _responseCodeToString(int code);
|
||||
|
||||
public:
|
||||
static const __FlashStringHelper * responseCodeToString(int code);
|
||||
|
||||
@@ -423,6 +527,8 @@ typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunc
|
||||
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, JsonVariant json)> ArJsonRequestHandlerFunction; // added by proddy
|
||||
|
||||
class AsyncWebServer {
|
||||
protected:
|
||||
AsyncServer _server;
|
||||
@@ -452,7 +558,10 @@ class AsyncWebServer {
|
||||
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, 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);
|
||||
|
||||
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);
|
||||
|
||||
@@ -480,8 +589,12 @@ public:
|
||||
_headers.emplace_back(name, value);
|
||||
}
|
||||
|
||||
ConstIterator begin() const { return _headers.begin(); }
|
||||
ConstIterator end() const { return _headers.end(); }
|
||||
ConstIterator begin() const {
|
||||
return _headers.begin();
|
||||
}
|
||||
ConstIterator end() const {
|
||||
return _headers.end();
|
||||
}
|
||||
|
||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||
DefaultHeaders & operator=(DefaultHeaders const &) = delete;
|
||||
|
||||
@@ -63,14 +63,49 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
||||
, _itemBuffer(0)
|
||||
, _itemBufferIndex(0)
|
||||
, _itemIsFile(false)
|
||||
, _tempObject(NULL)
|
||||
{
|
||||
c->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
|
||||
c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this);
|
||||
c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this);
|
||||
c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this);
|
||||
c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this);
|
||||
c->onPoll([](void *r, AsyncClient* c){ (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this);
|
||||
, _tempObject(NULL) {
|
||||
c->onError(
|
||||
[](void * r, AsyncClient * c, int8_t error) {
|
||||
(void)c;
|
||||
AsyncWebServerRequest * req = (AsyncWebServerRequest *)r;
|
||||
req->_onError(error);
|
||||
},
|
||||
this);
|
||||
c->onAck(
|
||||
[](void * r, AsyncClient * c, size_t len, uint32_t time) {
|
||||
(void)c;
|
||||
AsyncWebServerRequest * req = (AsyncWebServerRequest *)r;
|
||||
req->_onAck(len, time);
|
||||
},
|
||||
this);
|
||||
c->onDisconnect(
|
||||
[](void * r, AsyncClient * c) {
|
||||
AsyncWebServerRequest * req = (AsyncWebServerRequest *)r;
|
||||
req->_onDisconnect();
|
||||
delete c;
|
||||
},
|
||||
this);
|
||||
c->onTimeout(
|
||||
[](void * r, AsyncClient * c, uint32_t time) {
|
||||
(void)c;
|
||||
AsyncWebServerRequest * req = (AsyncWebServerRequest *)r;
|
||||
req->_onTimeout(time);
|
||||
},
|
||||
this);
|
||||
c->onData(
|
||||
[](void * r, AsyncClient * c, void * buf, size_t len) {
|
||||
(void)c;
|
||||
AsyncWebServerRequest * req = (AsyncWebServerRequest *)r;
|
||||
req->_onData(buf, len);
|
||||
},
|
||||
this);
|
||||
c->onPoll(
|
||||
[](void * r, AsyncClient * c) {
|
||||
(void)c;
|
||||
AsyncWebServerRequest * req = (AsyncWebServerRequest *)r;
|
||||
req->_onPoll();
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
AsyncWebServerRequest::~AsyncWebServerRequest() {
|
||||
@@ -96,13 +131,11 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
|
||||
if (_itemBuffer) {
|
||||
free(_itemBuffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_onData(void * buf, size_t len) {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
|
||||
if (_parseState < PARSE_REQ_BODY) {
|
||||
// Find new line in buf
|
||||
char * str = (char *)buf;
|
||||
@@ -148,7 +181,8 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
_isPlainPost = true;
|
||||
} else if (_contentType == F("text/plain") && __is_param_char(((char *)buf)[0])) {
|
||||
size_t i = 0;
|
||||
while (i<len && __is_param_char(((char*)buf)[i++]));
|
||||
while (i < len && __is_param_char(((char *)buf)[i++]))
|
||||
;
|
||||
if (i < len && ((char *)buf)[i - 1] == '=') {
|
||||
_isPlainPost = true;
|
||||
}
|
||||
@@ -156,7 +190,8 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
}
|
||||
if (!_isPlainPost) {
|
||||
//check if authenticated before calling the body
|
||||
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
|
||||
if (_handler)
|
||||
_handler->handleBody(this, (uint8_t *)buf, len, _parsedLength, _contentLength);
|
||||
_parsedLength += len;
|
||||
} else if (needParse) {
|
||||
size_t i;
|
||||
@@ -171,8 +206,10 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
if (_parsedLength == _contentLength) {
|
||||
_parseState = PARSE_REQ_END;
|
||||
//check if authenticated before calling handleRequest and request auth instead
|
||||
if(_handler) _handler->handleRequest(this);
|
||||
else send(501);
|
||||
if (_handler)
|
||||
_handler->handleRequest(this);
|
||||
else
|
||||
send(501);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -180,16 +217,13 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_removeNotInterestingHeaders() {
|
||||
if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||
[](const String &str){ return str.equalsIgnoreCase(F("ANY")); }))
|
||||
if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [](const String & str) { return str.equalsIgnoreCase(F("ANY")); }))
|
||||
return; // nothing to do
|
||||
|
||||
for(auto iter = std::begin(_headers); iter != std::end(_headers); )
|
||||
{
|
||||
for (auto iter = std::begin(_headers); iter != std::end(_headers);) {
|
||||
const auto name = iter->name();
|
||||
|
||||
if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
|
||||
if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String & str) { return str.equalsIgnoreCase(name); }))
|
||||
iter = _headers.erase(iter);
|
||||
else
|
||||
iter++;
|
||||
@@ -260,9 +294,11 @@ void AsyncWebServerRequest::_addGetParams(const String& params){
|
||||
size_t start = 0;
|
||||
while (start < params.length()) {
|
||||
int end = params.indexOf('&', start);
|
||||
if (end < 0) end = params.length();
|
||||
if (end < 0)
|
||||
end = params.length();
|
||||
int equal = params.indexOf('=', start);
|
||||
if (equal < 0 || equal > end) equal = end;
|
||||
if (equal < 0 || equal > end)
|
||||
equal = end;
|
||||
String name = params.substring(start, equal);
|
||||
String value = equal + 1 < end ? params.substring(equal + 1, end) : String();
|
||||
_addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value)));
|
||||
@@ -315,17 +351,19 @@ bool strContains(const String &src, const String &find, bool mindcase = true) {
|
||||
const int slen = src.length();
|
||||
const int flen = find.length();
|
||||
|
||||
if (slen < flen) return false;
|
||||
if (slen < flen)
|
||||
return false;
|
||||
while (pos <= (slen - flen)) {
|
||||
for (i = 0; i < flen; i++) {
|
||||
if (mindcase) {
|
||||
if (src[pos+i] != find[i]) i = flen + 1; // no match
|
||||
}
|
||||
else if (tolower(src[pos+i]) != tolower(find[i])) {
|
||||
if (src[pos + i] != find[i])
|
||||
i = flen + 1; // no match
|
||||
} else if (tolower(src[pos + i]) != tolower(find[i])) {
|
||||
i = flen + 1; // no match
|
||||
}
|
||||
}
|
||||
if (i == flen) return true;
|
||||
if (i == flen)
|
||||
return true;
|
||||
pos++;
|
||||
}
|
||||
return false;
|
||||
@@ -414,7 +452,14 @@ enum {
|
||||
};
|
||||
|
||||
void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
|
||||
#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0)
|
||||
#define itemWriteByte(b) \
|
||||
do { \
|
||||
_itemSize++; \
|
||||
if (_itemIsFile) \
|
||||
_handleUploadByte(b, last); \
|
||||
else \
|
||||
_itemValue += (char)(b); \
|
||||
} while (0)
|
||||
|
||||
if (!_parsedLength) {
|
||||
_multiParseState = EXPECT_BOUNDARY;
|
||||
@@ -500,21 +545,27 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||
} else if (_multiParseState == EXPECT_FEED1) {
|
||||
if (data != '\n') {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); _parseMultipartPostByte(data, last);
|
||||
itemWriteByte('\r');
|
||||
_parseMultipartPostByte(data, last);
|
||||
} else {
|
||||
_multiParseState = EXPECT_DASH1;
|
||||
}
|
||||
} else if (_multiParseState == EXPECT_DASH1) {
|
||||
if (data != '-') {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last);
|
||||
itemWriteByte('\r');
|
||||
itemWriteByte('\n');
|
||||
_parseMultipartPostByte(data, last);
|
||||
} else {
|
||||
_multiParseState = EXPECT_DASH2;
|
||||
}
|
||||
} else if (_multiParseState == EXPECT_DASH2) {
|
||||
if (data != '-') {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last);
|
||||
itemWriteByte('\r');
|
||||
itemWriteByte('\n');
|
||||
itemWriteByte('-');
|
||||
_parseMultipartPostByte(data, last);
|
||||
} else {
|
||||
_multiParseState = BOUNDARY_OR_DATA;
|
||||
_boundaryPosition = 0;
|
||||
@@ -522,7 +573,10 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||
} else if (_multiParseState == BOUNDARY_OR_DATA) {
|
||||
if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||
itemWriteByte('\r');
|
||||
itemWriteByte('\n');
|
||||
itemWriteByte('-');
|
||||
itemWriteByte('-');
|
||||
uint8_t i;
|
||||
for (i = 0; i < _boundaryPosition; i++)
|
||||
itemWriteByte(_boundary.c_str()[i]);
|
||||
@@ -534,7 +588,8 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||
} else {
|
||||
if (_itemSize) {
|
||||
//check if authenticated before calling the upload
|
||||
if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
|
||||
if (_handler)
|
||||
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
|
||||
_itemBufferIndex = 0;
|
||||
_addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize));
|
||||
}
|
||||
@@ -556,8 +611,13 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||
_multiParseState = PARSING_FINISHED;
|
||||
} else {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
|
||||
itemWriteByte('\r');
|
||||
itemWriteByte('\n');
|
||||
itemWriteByte('-');
|
||||
itemWriteByte('-');
|
||||
uint8_t i;
|
||||
for (i = 0; i < _boundary.length(); i++)
|
||||
itemWriteByte(_boundary.c_str()[i]);
|
||||
_parseMultipartPostByte(data, last);
|
||||
}
|
||||
} else if (_multiParseState == EXPECT_FEED2) {
|
||||
@@ -566,9 +626,15 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||
_itemIsFile = false;
|
||||
} else {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
|
||||
itemWriteByte('\r'); _parseMultipartPostByte(data, last);
|
||||
itemWriteByte('\r');
|
||||
itemWriteByte('\n');
|
||||
itemWriteByte('-');
|
||||
itemWriteByte('-');
|
||||
uint8_t i;
|
||||
for (i = 0; i < _boundary.length(); i++)
|
||||
itemWriteByte(_boundary.c_str()[i]);
|
||||
itemWriteByte('\r');
|
||||
_parseMultipartPostByte(data, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -600,10 +666,13 @@ void AsyncWebServerRequest::_parseLine(){
|
||||
_parseState = PARSE_REQ_BODY;
|
||||
} else {
|
||||
_parseState = PARSE_REQ_END;
|
||||
if(_handler) _handler->handleRequest(this);
|
||||
else send(501);
|
||||
if (_handler)
|
||||
_handler->handleRequest(this);
|
||||
else
|
||||
send(501);
|
||||
}
|
||||
} else _parseReqHeader();
|
||||
} else
|
||||
_parseReqHeader();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,8 +694,7 @@ bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const {
|
||||
}
|
||||
|
||||
AsyncWebHeader * AsyncWebServerRequest::getHeader(const String & name) {
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
|
||||
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader & header) { return header.name().equalsIgnoreCase(name); });
|
||||
|
||||
if (iter == std::end(_headers))
|
||||
return nullptr;
|
||||
@@ -635,8 +703,7 @@ AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) {
|
||||
}
|
||||
|
||||
const AsyncWebHeader * AsyncWebServerRequest::getHeader(const String & name) const {
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
|
||||
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader & header) { return header.name().equalsIgnoreCase(name); });
|
||||
|
||||
if (iter == std::end(_headers))
|
||||
return nullptr;
|
||||
@@ -720,8 +787,7 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::addInterestingHeader(const String & name) {
|
||||
if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
|
||||
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
|
||||
if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String & str) { return str.equalsIgnoreCase(name); }))
|
||||
_interestingHeaders.push_back(name);
|
||||
}
|
||||
|
||||
@@ -736,8 +802,7 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
|
||||
delete response;
|
||||
_response = NULL;
|
||||
send(500);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_client->setRxTimeout(0);
|
||||
_response->_respond(this);
|
||||
}
|
||||
@@ -747,13 +812,15 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const St
|
||||
return new AsyncBasicResponse(code, contentType, content);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (fs.exists(path) || (!download && fs.exists(path + F(".gz"))))
|
||||
return new AsyncFileResponse(fs, path, contentType, download, callback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (content == true)
|
||||
return new AsyncFileResponse(content, path, contentType, download, callback);
|
||||
return NULL;
|
||||
@@ -763,11 +830,13 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, co
|
||||
return new AsyncStreamResponse(stream, contentType, len, callback);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
|
||||
return new AsyncCallbackResponse(contentType, len, callback, templateCallback);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
|
||||
if (_version)
|
||||
return new AsyncChunkedResponse(contentType, callback, templateCallback);
|
||||
return new AsyncCallbackResponse(contentType, 0, callback, templateCallback);
|
||||
@@ -777,7 +846,8 @@ AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& c
|
||||
return new AsyncResponseStream(contentType, bufferSize);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) {
|
||||
return new AsyncProgmemResponse(code, contentType, content, len, callback);
|
||||
}
|
||||
|
||||
@@ -792,13 +862,15 @@ void AsyncWebServerRequest::send(int code, const String& contentType, const Stri
|
||||
void AsyncWebServerRequest::send(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) {
|
||||
send(beginResponse(fs, path, contentType, download, callback));
|
||||
} else send(404);
|
||||
} else
|
||||
send(404);
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::send(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (content == true) {
|
||||
send(beginResponse(content, path, contentType, download, callback));
|
||||
} else send(404);
|
||||
} else
|
||||
send(404);
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback) {
|
||||
@@ -962,32 +1034,49 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
|
||||
|
||||
|
||||
const __FlashStringHelper * AsyncWebServerRequest::methodToString() const {
|
||||
if(_method == HTTP_ANY) return F("ANY");
|
||||
else if(_method & HTTP_GET) return F("GET");
|
||||
else if(_method & HTTP_POST) return F("POST");
|
||||
else if(_method & HTTP_DELETE) return F("DELETE");
|
||||
else if(_method & HTTP_PUT) return F("PUT");
|
||||
else if(_method & HTTP_PATCH) return F("PATCH");
|
||||
else if(_method & HTTP_HEAD) return F("HEAD");
|
||||
else if(_method & HTTP_OPTIONS) return F("OPTIONS");
|
||||
if (_method == HTTP_ANY)
|
||||
return F("ANY");
|
||||
else if (_method & HTTP_GET)
|
||||
return F("GET");
|
||||
else if (_method & HTTP_POST)
|
||||
return F("POST");
|
||||
else if (_method & HTTP_DELETE)
|
||||
return F("DELETE");
|
||||
else if (_method & HTTP_PUT)
|
||||
return F("PUT");
|
||||
else if (_method & HTTP_PATCH)
|
||||
return F("PATCH");
|
||||
else if (_method & HTTP_HEAD)
|
||||
return F("HEAD");
|
||||
else if (_method & HTTP_OPTIONS)
|
||||
return F("OPTIONS");
|
||||
return F("UNKNOWN");
|
||||
}
|
||||
|
||||
const __FlashStringHelper * AsyncWebServerRequest::requestedConnTypeToString() const {
|
||||
switch (_reqconntype) {
|
||||
case RCT_NOT_USED: return F("RCT_NOT_USED");
|
||||
case RCT_DEFAULT: return F("RCT_DEFAULT");
|
||||
case RCT_HTTP: return F("RCT_HTTP");
|
||||
case RCT_WS: return F("RCT_WS");
|
||||
case RCT_EVENT: return F("RCT_EVENT");
|
||||
default: return F("ERROR");
|
||||
case RCT_NOT_USED:
|
||||
return F("RCT_NOT_USED");
|
||||
case RCT_DEFAULT:
|
||||
return F("RCT_DEFAULT");
|
||||
case RCT_HTTP:
|
||||
return F("RCT_HTTP");
|
||||
case RCT_WS:
|
||||
return F("RCT_WS");
|
||||
case RCT_EVENT:
|
||||
return F("RCT_EVENT");
|
||||
default:
|
||||
return F("ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) {
|
||||
bool res = false;
|
||||
if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true;
|
||||
if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true;
|
||||
if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true;
|
||||
if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype))
|
||||
res = true;
|
||||
if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype))
|
||||
res = true;
|
||||
if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype))
|
||||
res = true;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -34,11 +34,14 @@
|
||||
class AsyncBasicResponse : public AsyncWebServerResponse {
|
||||
private:
|
||||
String _content;
|
||||
|
||||
public:
|
||||
AsyncBasicResponse(int code, const String & contentType = String(), const String & content = String());
|
||||
void _respond(AsyncWebServerRequest * request);
|
||||
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 {
|
||||
@@ -51,14 +54,20 @@ class AsyncAbstractResponse: public AsyncWebServerResponse {
|
||||
std::vector<uint8_t> _cache;
|
||||
size_t _readDataFromCacheOrContent(uint8_t * data, const size_t len);
|
||||
size_t _fillBufferAndProcessTemplates(uint8_t * buf, size_t maxLen);
|
||||
|
||||
protected:
|
||||
AwsTemplateProcessor _callback;
|
||||
|
||||
public:
|
||||
AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr);
|
||||
void _respond(AsyncWebServerRequest * request);
|
||||
size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time);
|
||||
bool _sourceValid() const { return false; }
|
||||
virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
|
||||
bool _sourceValid() const {
|
||||
return false;
|
||||
}
|
||||
virtual size_t _fillBuffer(uint8_t * buf __attribute__((unused)), size_t maxLen __attribute__((unused))) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef TEMPLATE_PLACEHOLDER
|
||||
@@ -69,24 +78,31 @@ class AsyncAbstractResponse: public AsyncWebServerResponse {
|
||||
class AsyncFileResponse : public AsyncAbstractResponse {
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
|
||||
private:
|
||||
File _content;
|
||||
String _path;
|
||||
void _setContentType(const String & path);
|
||||
|
||||
public:
|
||||
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();
|
||||
bool _sourceValid() const { return !!(_content); }
|
||||
bool _sourceValid() const {
|
||||
return !!(_content);
|
||||
}
|
||||
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
|
||||
};
|
||||
|
||||
class AsyncStreamResponse : public AsyncAbstractResponse {
|
||||
private:
|
||||
Stream * _content;
|
||||
|
||||
public:
|
||||
AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr);
|
||||
bool _sourceValid() const { return !!(_content); }
|
||||
bool _sourceValid() const {
|
||||
return !!(_content);
|
||||
}
|
||||
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
|
||||
};
|
||||
|
||||
@@ -94,9 +110,12 @@ class AsyncCallbackResponse: public AsyncAbstractResponse {
|
||||
private:
|
||||
AwsResponseFiller _content;
|
||||
size_t _filledLength;
|
||||
|
||||
public:
|
||||
AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||
bool _sourceValid() const { return !!(_content); }
|
||||
bool _sourceValid() const {
|
||||
return !!(_content);
|
||||
}
|
||||
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
|
||||
};
|
||||
|
||||
@@ -104,9 +123,12 @@ class AsyncChunkedResponse: public AsyncAbstractResponse {
|
||||
private:
|
||||
AwsResponseFiller _content;
|
||||
size_t _filledLength;
|
||||
|
||||
public:
|
||||
AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||
bool _sourceValid() const { return !!(_content); }
|
||||
bool _sourceValid() const {
|
||||
return !!(_content);
|
||||
}
|
||||
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
|
||||
};
|
||||
|
||||
@@ -114,9 +136,12 @@ class AsyncProgmemResponse: public AsyncAbstractResponse {
|
||||
private:
|
||||
const uint8_t * _content;
|
||||
size_t _readLength;
|
||||
|
||||
public:
|
||||
AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr);
|
||||
bool _sourceValid() const { return true; }
|
||||
bool _sourceValid() const {
|
||||
return true;
|
||||
}
|
||||
virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override;
|
||||
};
|
||||
|
||||
@@ -125,10 +150,13 @@ class cbuf;
|
||||
class AsyncResponseStream : public AsyncAbstractResponse, public Print {
|
||||
private:
|
||||
std::unique_ptr<cbuf> _content;
|
||||
|
||||
public:
|
||||
AsyncResponseStream(const String & contentType, size_t bufferSize);
|
||||
~AsyncResponseStream();
|
||||
bool _sourceValid() const { return (_state < RESPONSE_END); }
|
||||
bool _sourceValid() const {
|
||||
return (_state < RESPONSE_END);
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
#include "cbuf.h"
|
||||
|
||||
// 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);
|
||||
while (count--)
|
||||
if (*p++ == static_cast<unsigned char>(ch))
|
||||
@@ -42,47 +41,88 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) {
|
||||
|
||||
const __FlashStringHelper * AsyncWebServerResponse::responseCodeToString(int code) {
|
||||
switch (code) {
|
||||
case 100: return F("Continue");
|
||||
case 101: return F("Switching Protocols");
|
||||
case 200: return F("OK");
|
||||
case 201: return F("Created");
|
||||
case 202: return F("Accepted");
|
||||
case 203: return F("Non-Authoritative Information");
|
||||
case 204: return F("No Content");
|
||||
case 205: return F("Reset Content");
|
||||
case 206: return F("Partial Content");
|
||||
case 300: return F("Multiple Choices");
|
||||
case 301: return F("Moved Permanently");
|
||||
case 302: return F("Found");
|
||||
case 303: return F("See Other");
|
||||
case 304: return F("Not Modified");
|
||||
case 305: return F("Use Proxy");
|
||||
case 307: return F("Temporary Redirect");
|
||||
case 400: return F("Bad Request");
|
||||
case 401: return F("Unauthorized");
|
||||
case 402: return F("Payment Required");
|
||||
case 403: return F("Forbidden");
|
||||
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("");
|
||||
case 100:
|
||||
return F("Continue");
|
||||
case 101:
|
||||
return F("Switching Protocols");
|
||||
case 200:
|
||||
return F("OK");
|
||||
case 201:
|
||||
return F("Created");
|
||||
case 202:
|
||||
return F("Accepted");
|
||||
case 203:
|
||||
return F("Non-Authoritative Information");
|
||||
case 204:
|
||||
return F("No Content");
|
||||
case 205:
|
||||
return F("Reset Content");
|
||||
case 206:
|
||||
return F("Partial Content");
|
||||
case 300:
|
||||
return F("Multiple Choices");
|
||||
case 301:
|
||||
return F("Moved Permanently");
|
||||
case 302:
|
||||
return F("Found");
|
||||
case 303:
|
||||
return F("See Other");
|
||||
case 304:
|
||||
return F("Not Modified");
|
||||
case 305:
|
||||
return F("Use Proxy");
|
||||
case 307:
|
||||
return F("Temporary Redirect");
|
||||
case 400:
|
||||
return F("Bad Request");
|
||||
case 401:
|
||||
return F("Unauthorized");
|
||||
case 402:
|
||||
return F("Payment Required");
|
||||
case 403:
|
||||
return F("Forbidden");
|
||||
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,8 +136,7 @@ AsyncWebServerResponse::AsyncWebServerResponse()
|
||||
, _sentLength(0)
|
||||
, _ackedLength(0)
|
||||
, _writtenLength(0)
|
||||
, _state(RESPONSE_SETUP)
|
||||
{
|
||||
, _state(RESPONSE_SETUP) {
|
||||
for (const auto & header : DefaultHeaders::Instance()) {
|
||||
_headers.emplace_back(header);
|
||||
}
|
||||
@@ -157,12 +196,28 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
||||
return out;
|
||||
}
|
||||
|
||||
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
|
||||
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
|
||||
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; }
|
||||
bool AsyncWebServerResponse::_started() const {
|
||||
return _state > RESPONSE_SETUP;
|
||||
}
|
||||
bool AsyncWebServerResponse::_finished() const {
|
||||
return _state > RESPONSE_WAIT_ACK;
|
||||
}
|
||||
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
|
||||
@@ -245,8 +300,8 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint
|
||||
* 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
|
||||
if (callback) {
|
||||
_contentLength = 0;
|
||||
@@ -319,7 +374,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
return 0;
|
||||
}
|
||||
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++] = '\n';
|
||||
outLen += readLen;
|
||||
@@ -365,8 +421,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
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
|
||||
const size_t readFromCache = std::min(len, _cache.size());
|
||||
if (readFromCache) {
|
||||
@@ -379,8 +434,7 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s
|
||||
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)
|
||||
return _fillBuffer(data, len);
|
||||
|
||||
@@ -389,8 +443,10 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
// Now we've read 'len' bytes, either from cache or from file
|
||||
// Search for template placeholders
|
||||
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]
|
||||
uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
|
||||
while ((pTemplateStart < &data[len])
|
||||
&& (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
|
||||
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
|
||||
String paramName;
|
||||
@@ -410,7 +466,8 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
}
|
||||
} 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);
|
||||
const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
||||
const size_t readFromCacheOrContent =
|
||||
_readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
||||
if (readFromCacheOrContent) {
|
||||
pTemplateEnd = (uint8_t *)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
|
||||
if (pTemplateEnd) {
|
||||
@@ -420,18 +477,15 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
||||
// Copy remaining read-ahead data into cache
|
||||
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||
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
|
||||
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||
++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;
|
||||
}
|
||||
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;
|
||||
if (paramName.length()) {
|
||||
// call callback and replace with result.
|
||||
@@ -488,29 +542,49 @@ void AsyncFileResponse::_setContentType(const String& path){
|
||||
extern const __FlashStringHelper * getContentType(const String & path);
|
||||
_contentType = getContentType(path);
|
||||
#else
|
||||
if (path.endsWith(F(".html"))) _contentType = F("text/html");
|
||||
else if (path.endsWith(F(".htm"))) _contentType = F("text/html");
|
||||
else if (path.endsWith(F(".css"))) _contentType = F("text/css");
|
||||
else if (path.endsWith(F(".json"))) _contentType = F("application/json");
|
||||
else if (path.endsWith(F(".js"))) _contentType = F("application/javascript");
|
||||
else if (path.endsWith(F(".png"))) _contentType = F("image/png");
|
||||
else if (path.endsWith(F(".gif"))) _contentType = F("image/gif");
|
||||
else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg");
|
||||
else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon");
|
||||
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");
|
||||
if (path.endsWith(F(".html")))
|
||||
_contentType = F("text/html");
|
||||
else if (path.endsWith(F(".htm")))
|
||||
_contentType = F("text/html");
|
||||
else if (path.endsWith(F(".css")))
|
||||
_contentType = F("text/css");
|
||||
else if (path.endsWith(F(".json")))
|
||||
_contentType = F("application/json");
|
||||
else if (path.endsWith(F(".js")))
|
||||
_contentType = F("application/javascript");
|
||||
else if (path.endsWith(F(".png")))
|
||||
_contentType = F("image/png");
|
||||
else if (path.endsWith(F(".gif")))
|
||||
_contentType = F("image/gif");
|
||||
else if (path.endsWith(F(".jpg")))
|
||||
_contentType = F("image/jpeg");
|
||||
else if (path.endsWith(F(".ico")))
|
||||
_contentType = F("image/x-icon");
|
||||
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
|
||||
}
|
||||
|
||||
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;
|
||||
_path = path;
|
||||
|
||||
@@ -544,7 +618,8 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& c
|
||||
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;
|
||||
_path = path;
|
||||
|
||||
@@ -583,7 +658,8 @@ size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
* 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;
|
||||
_content = &stream;
|
||||
_contentLength = len;
|
||||
@@ -603,7 +679,8 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
* 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;
|
||||
_content = callback;
|
||||
_contentLength = len;
|
||||
@@ -625,7 +702,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
* 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;
|
||||
_content = callback;
|
||||
_contentLength = 0;
|
||||
@@ -647,7 +725,8 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
* 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;
|
||||
_content = content;
|
||||
_contentType = contentType;
|
||||
@@ -667,13 +746,11 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
_contentLength = 0;
|
||||
_contentType = contentType;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "WebHandlerImpl.h"
|
||||
#include "AsyncJson.h"
|
||||
|
||||
bool ON_STA_FILTER(AsyncWebServerRequest * request) {
|
||||
return WiFi.localIP() == request->client()->localIP();
|
||||
@@ -38,12 +39,12 @@ const char *fs::FileOpenMode::append = "a";
|
||||
AsyncWebServer::AsyncWebServer(uint16_t port)
|
||||
: _server(port)
|
||||
, _rewrites(LinkedList<AsyncWebRewrite *>([](AsyncWebRewrite * r) { delete r; }))
|
||||
, _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
|
||||
{
|
||||
, _handlers(LinkedList<AsyncWebHandler *>([](AsyncWebHandler * h) { delete h; })) {
|
||||
_catchAllHandler = new AsyncCallbackWebHandler();
|
||||
if (_catchAllHandler == NULL)
|
||||
return;
|
||||
_server.onClient([](void *s, AsyncClient* c){
|
||||
_server.onClient(
|
||||
[](void * s, AsyncClient * c) {
|
||||
if (c == NULL)
|
||||
return;
|
||||
c->setRxTimeout(3);
|
||||
@@ -53,13 +54,15 @@ AsyncWebServer::AsyncWebServer(uint16_t port)
|
||||
c->free();
|
||||
delete c;
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
AsyncWebServer::~AsyncWebServer() {
|
||||
reset();
|
||||
end();
|
||||
if(_catchAllHandler) delete _catchAllHandler;
|
||||
if (_catchAllHandler)
|
||||
delete _catchAllHandler;
|
||||
}
|
||||
|
||||
AsyncWebRewrite & AsyncWebServer::addRewrite(AsyncWebRewrite * rewrite) {
|
||||
@@ -129,7 +132,11 @@ 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,
|
||||
WebRequestMethodComposite method,
|
||||
ArRequestHandlerFunction onRequest,
|
||||
ArUploadHandlerFunction onUpload,
|
||||
ArBodyHandlerFunction onBody) {
|
||||
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
|
||||
handler->setUri(uri);
|
||||
handler->setMethod(method);
|
||||
@@ -140,7 +147,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
|
||||
return *handler;
|
||||
}
|
||||
|
||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
|
||||
AsyncCallbackWebHandler &
|
||||
AsyncWebServer::on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) {
|
||||
AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler();
|
||||
handler->setUri(uri);
|
||||
handler->setMethod(method);
|
||||
@@ -167,6 +175,12 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFun
|
||||
return *handler;
|
||||
}
|
||||
|
||||
// added by proddy
|
||||
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);
|
||||
@@ -195,4 +209,3 @@ void AsyncWebServer::reset(){
|
||||
_catchAllHandler->onBody(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
#include "AuthenticationService.h"
|
||||
|
||||
AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _signInHandler(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); }) {
|
||||
: _securityManager(securityManager) {
|
||||
server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { verifyAuthorization(request); });
|
||||
_signInHandler.setMethod(HTTP_POST);
|
||||
_signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
|
||||
server->addHandler(&_signInHandler);
|
||||
server->on(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,17 +9,13 @@
|
||||
#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization"
|
||||
#define SIGN_IN_PATH "/rest/signIn"
|
||||
|
||||
#define MAX_AUTHENTICATION_SIZE 256
|
||||
|
||||
class AuthenticationService {
|
||||
public:
|
||||
AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
AsyncCallbackJsonWebHandler _signInHandler;
|
||||
|
||||
// endpoint functions
|
||||
void signIn(AsyncWebServerRequest * request, JsonVariant json);
|
||||
void verifyAuthorization(AsyncWebServerRequest * request);
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
static char last_modified[50];
|
||||
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) {
|
||||
// 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) {
|
||||
@@ -37,6 +37,7 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse_P(200, contentType, content, len);
|
||||
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
// response->addHeader("Content-Encoding", "br"); // only works over HTTPS
|
||||
// response->addHeader("Cache-Control", "public, immutable, max-age=31536000");
|
||||
@@ -46,10 +47,10 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
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"
|
||||
// OPTIONS get a straight up 200 response
|
||||
if (uri.equals("/index.html")) {
|
||||
if (strncmp(uri, "/index.html", 11) == 0) {
|
||||
server->onNotFound([requestHandler](AsyncWebServerRequest * request) {
|
||||
if (request->method() == HTTP_GET) {
|
||||
requestHandler(request);
|
||||
|
||||
@@ -34,6 +34,9 @@ class FSPersistence {
|
||||
|
||||
// If we reach here we have not been successful in loading the config,
|
||||
// hard-coded emergency defaults are now applied.
|
||||
#ifdef EMSESP_DEBUG
|
||||
Serial.println("Applying defaults to " + String(_filePath));
|
||||
#endif
|
||||
applyDefaults();
|
||||
writeToFS(); // added to make sure the initial file is created
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ class HttpEndpoint {
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T> * _statefulService;
|
||||
|
||||
AsyncCallbackJsonWebHandler * handler;
|
||||
|
||||
public:
|
||||
HttpEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
@@ -30,12 +28,10 @@ class HttpEndpoint {
|
||||
: _stateReader(stateReader)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _statefulService(statefulService) {
|
||||
// Create hander for both GET and POST endpoints
|
||||
handler = new AsyncCallbackJsonWebHandler(servicePath,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request,
|
||||
JsonVariant json) { handleRequest(request, json); },
|
||||
// Create handler for both GET and POST endpoints
|
||||
server->on(servicePath.c_str(),
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { handleRequest(request, json); },
|
||||
authenticationPredicate));
|
||||
server->addHandler(handler);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -5,13 +5,10 @@
|
||||
NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _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) {
|
||||
_timeHandler.setMethod(HTTP_POST);
|
||||
_timeHandler.setMaxContentLength(MAX_TIME_SIZE);
|
||||
server->addHandler(&_timeHandler);
|
||||
server->on(TIME_PATH,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { configureTime(request, json); },
|
||||
AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
addUpdateHandler([this] { configureNTP(); }, false);
|
||||
|
||||
@@ -24,9 +24,8 @@
|
||||
#endif
|
||||
|
||||
#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"
|
||||
|
||||
class NTPSettings {
|
||||
@@ -50,7 +49,6 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
|
||||
private:
|
||||
HttpEndpoint<NTPSettings> _httpEndpoint;
|
||||
FSPersistence<NTPSettings> _fsPersistence;
|
||||
AsyncCallbackJsonWebHandler _timeHandler;
|
||||
bool _connected;
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
|
||||
@@ -368,13 +368,7 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
case ARDUINO_EVENT_ETH_GOT_IP6:
|
||||
if (emsesp::EMSESP::system_.ethernet_connected()) {
|
||||
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);
|
||||
break;
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ class AsyncWebServerResponse {
|
||||
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, 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 {
|
||||
protected:
|
||||
@@ -232,6 +233,7 @@ class AsyncWebServer {
|
||||
}
|
||||
|
||||
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
@@ -4,4 +4,4 @@ enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.1.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.1.1.cjs
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "^2.8.0",
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.18.2",
|
||||
"itty-router": "^4.0.27",
|
||||
"express": "^4.18.3",
|
||||
"itty-router": "^4.2.0",
|
||||
"multer": "^1.4.5-lts.1"
|
||||
},
|
||||
"packageManager": "yarn@4.1.0",
|
||||
"packageManager": "yarn@4.1.1",
|
||||
"devDependencies": {
|
||||
"@types/multer": "^1.4.11"
|
||||
}
|
||||
|
||||
@@ -139,8 +139,8 @@ __metadata:
|
||||
"@msgpack/msgpack": "npm:^2.8.0"
|
||||
"@types/multer": "npm:^1.4.11"
|
||||
compression: "npm:^1.7.4"
|
||||
express: "npm:^4.18.2"
|
||||
itty-router: "npm:^4.0.27"
|
||||
express: "npm:^4.18.3"
|
||||
itty-router: "npm:^4.2.0"
|
||||
multer: "npm:^1.4.5-lts.1"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -159,12 +159,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"body-parser@npm:1.20.1":
|
||||
version: 1.20.1
|
||||
resolution: "body-parser@npm:1.20.1"
|
||||
"body-parser@npm:1.20.2":
|
||||
version: 1.20.2
|
||||
resolution: "body-parser@npm:1.20.2"
|
||||
dependencies:
|
||||
bytes: "npm:3.1.2"
|
||||
content-type: "npm:~1.0.4"
|
||||
content-type: "npm:~1.0.5"
|
||||
debug: "npm:2.6.9"
|
||||
depd: "npm:2.0.0"
|
||||
destroy: "npm:1.2.0"
|
||||
@@ -172,10 +172,10 @@ __metadata:
|
||||
iconv-lite: "npm:0.4.24"
|
||||
on-finished: "npm:2.4.1"
|
||||
qs: "npm:6.11.0"
|
||||
raw-body: "npm:2.5.1"
|
||||
raw-body: "npm:2.5.2"
|
||||
type-is: "npm:~1.6.18"
|
||||
unpipe: "npm:1.0.0"
|
||||
checksum: 10/5f8d128022a2fb8b6e7990d30878a0182f300b70e46b3f9d358a9433ad6275f0de46add6d63206da3637c01c3b38b6111a7480f7e7ac2e9f7b989f6133fe5510
|
||||
checksum: 10/3cf171b82190cf91495c262b073e425fc0d9e25cc2bf4540d43f7e7bbca27d6a9eae65ca367b6ef3993eea261159d9d2ab37ce444e8979323952e12eb3df319a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -265,7 +265,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"content-type@npm:~1.0.4":
|
||||
"content-type@npm:~1.0.4, content-type@npm:~1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "content-type@npm:1.0.5"
|
||||
checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662
|
||||
@@ -355,13 +355,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"express@npm:^4.18.2":
|
||||
version: 4.18.2
|
||||
resolution: "express@npm:4.18.2"
|
||||
"express@npm:^4.18.3":
|
||||
version: 4.18.3
|
||||
resolution: "express@npm:4.18.3"
|
||||
dependencies:
|
||||
accepts: "npm:~1.3.8"
|
||||
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-type: "npm:~1.0.4"
|
||||
cookie: "npm:0.5.0"
|
||||
@@ -390,7 +390,7 @@ __metadata:
|
||||
type-is: "npm:~1.6.18"
|
||||
utils-merge: "npm:1.0.1"
|
||||
vary: "npm:~1.1.2"
|
||||
checksum: 10/869ae89ed6ff4bed7b373079dc58e5dddcf2915a2669b36037ff78c99d675ae930e5fe052b35c24f56557d28a023bb1cbe3e2f2fb87eaab96a1cedd7e597809d
|
||||
checksum: 10/0bf4656d0020cdc477aec884c6245dceea78992f6c747c899c87dbb0598474658d4130a29c80f02c99d1f0d6ebef706e7131c70c5454c3e07aba761c5bd3d627
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -526,10 +526,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"itty-router@npm:^4.0.27":
|
||||
version: 4.0.27
|
||||
resolution: "itty-router@npm:4.0.27"
|
||||
checksum: 10/ebb959388b1033f3d80ba2575c2d90fa649c1d5370d977879513cc46e8fd78159b7140d2a66853af6be98f7d740f8609a2c5aa7381506eaa1f1a46268fd2a95f
|
||||
"itty-router@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "itty-router@npm:4.2.0"
|
||||
checksum: 10/39ee6c8b87f77de3918a9b3c1acaf2047626a69b954d7e1f5b9ab7ab9a2bf7e43c97b99ab86496aa9f5b139b024eebaf3b423f51fe000a8a0510901aeea10604
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -710,15 +710,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"raw-body@npm:2.5.1":
|
||||
version: 2.5.1
|
||||
resolution: "raw-body@npm:2.5.1"
|
||||
"raw-body@npm:2.5.2":
|
||||
version: 2.5.2
|
||||
resolution: "raw-body@npm:2.5.2"
|
||||
dependencies:
|
||||
bytes: "npm:3.1.2"
|
||||
http-errors: "npm:2.0.0"
|
||||
iconv-lite: "npm:0.4.24"
|
||||
unpipe: "npm:1.0.0"
|
||||
checksum: 10/280bedc12db3490ecd06f740bdcf66093a07535374b51331242382c0e130bb273ebb611b7bc4cba1b4b4e016cc7b1f4b05a6df885a6af39c2bc3b94c02291c84
|
||||
checksum: 10/863b5171e140546a4d99f349b720abac4410338e23df5e409cfcc3752538c9caf947ce382c89129ba976f71894bd38b5806c774edac35ebf168d02aa1ac11a95
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ extra_scripts =
|
||||
|
||||
[env]
|
||||
monitor_speed = 115200
|
||||
monitor_filters = direct, esp32_exception_decoder
|
||||
|
||||
upload_speed = 921600
|
||||
build_type = release
|
||||
lib_ldf_mode = chain+
|
||||
@@ -189,7 +191,7 @@ platform = native
|
||||
build_flags =
|
||||
-DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
|
||||
-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
|
||||
-D__linux__
|
||||
-std=gnu++11 -Og -ggdb
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# The response will be shown in the right panel
|
||||
|
||||
@host = http://ems-esp.local
|
||||
@host_dev = http://ems-esp2.local
|
||||
@host_dev = http://10.10.10.20
|
||||
|
||||
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg
|
||||
|
||||
@@ -95,3 +95,14 @@ Authorization: Bearer {{token}}
|
||||
###
|
||||
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
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
# run from top folder like `sh ./scripts/dump_entities.sh`
|
||||
rm -f dump_entities.csv
|
||||
make clean
|
||||
make ARGS=-DEMSESP_STANDALONE_DUMP
|
||||
make ARGS=-DEMSESP_STANDALONE
|
||||
echo "test entity_dump" | ./emsesp | python3 ./scripts/dump_entities.py > dump_entities.csv
|
||||
cat dump_entities.csv
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
@@ -238,7 +246,8 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
networkSettings.password = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("WiFi password updated");
|
||||
shell.println("WiFi password updated. Reconnecting...");
|
||||
to_app(shell).system_.wifi_reconnect();
|
||||
} else {
|
||||
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();
|
||||
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();
|
||||
});
|
||||
|
||||
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
|
||||
//
|
||||
@@ -594,13 +641,15 @@ void EMSESPShell::stopped() {
|
||||
// show welcome banner
|
||||
void EMSESPShell::display_banner() {
|
||||
println();
|
||||
printfln("┌───────────────────────────────────────┐");
|
||||
printfln("┌──────────────────────────────────────────┐");
|
||||
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("│ │");
|
||||
printfln("│ type %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("│ use %ssu%s to access Admin commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("└───────────────────────────────────────┘");
|
||||
printfln("└──────────────────────────────────────────┘");
|
||||
println();
|
||||
|
||||
// set console name
|
||||
|
||||
@@ -239,7 +239,8 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const ui
|
||||
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
|
||||
}
|
||||
|
||||
@@ -3023,7 +3024,7 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
|
||||
}
|
||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
||||
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;
|
||||
}
|
||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_BC400 || model() == EMSdevice::EMS_DEVICE_FLAG_RC300) {
|
||||
@@ -4506,9 +4507,11 @@ void Thermostat::register_device_values() {
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -4598,8 +4601,13 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
||||
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->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(
|
||||
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->tempautotemp,
|
||||
|
||||
@@ -1833,8 +1833,11 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_);
|
||||
#endif
|
||||
// removing fetch causes issue: https://github.com/emsesp/EMS-ESP32/issues/1420
|
||||
// tf.fetch_ = false;
|
||||
// removing fetch after start causes issue: https://github.com/emsesp/EMS-ESP32/issues/1420
|
||||
// continue retry the first 5 minutes, then disable (added 15.3.2024)
|
||||
if (uuid::get_uptime_sec() > 600) {
|
||||
tf.fetch_ = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (telegram->message_length > 0) {
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
|
||||
#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::log::thread_safe, "uuid-log 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)
|
||||
|
||||
if (!nvs_.begin("ems-esp", false, "nvs1")) { // try new partition on 16M flash first
|
||||
nvs_.begin("ems-esp", false, "nvs"); // fallback to first nvs
|
||||
if (!nvs_.begin("ems-esp", false, "nvs1")) { // try bigger nvs partition on 16M flash first
|
||||
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
|
||||
#endif
|
||||
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());
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ MAKE_WORD(users)
|
||||
MAKE_WORD(publish)
|
||||
MAKE_WORD(board_profile)
|
||||
MAKE_WORD(setvalue)
|
||||
MAKE_WORD(service)
|
||||
|
||||
// for commands
|
||||
MAKE_WORD(call)
|
||||
@@ -142,6 +143,7 @@ MAKE_WORD_CUSTOM(asterisks, "********")
|
||||
MAKE_WORD_CUSTOM(n_mandatory, "<n>")
|
||||
MAKE_WORD_CUSTOM(sensorid_optional, "[sensor ID]")
|
||||
MAKE_WORD_CUSTOM(id_optional, "[id|hc]")
|
||||
MAKE_WORD_CUSTOM(partitionname_optional, "[partitionsname]")
|
||||
MAKE_WORD_CUSTOM(data_optional, "[data]")
|
||||
MAKE_WORD_CUSTOM(nvs_optional, "[nvs]")
|
||||
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(password_prompt, "Password: ")
|
||||
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
|
||||
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_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_controlmode2, FL_(outdoor), FL_(room))
|
||||
MAKE_ENUM(enum_controlmode3, FL_(off), FL_(unmixed), FL_(unmixedIPM), FL_(mixed))
|
||||
|
||||
@@ -264,12 +264,40 @@ void System::store_nvs_values() {
|
||||
}
|
||||
|
||||
// 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...");
|
||||
}
|
||||
store_nvs_values();
|
||||
Shell::loop_all();
|
||||
delay(1000); // wait a second
|
||||
#ifndef EMSESP_STANDALONE
|
||||
ESP.restart();
|
||||
#endif
|
||||
}
|
||||
@@ -462,7 +490,8 @@ void System::button_OnDblClick(PButton & b) {
|
||||
|
||||
// button long press
|
||||
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
|
||||
@@ -1517,9 +1546,13 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
|
||||
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) {
|
||||
restart_requested(true);
|
||||
if (value != nullptr && value[0] == '\0') {
|
||||
EMSESP::system_.system_restart(value);
|
||||
} else {
|
||||
EMSESP::system_.system_restart();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ class System {
|
||||
std::string reset_reason(uint8_t cpu) const;
|
||||
|
||||
void store_nvs_values();
|
||||
void system_restart();
|
||||
void system_restart(const char * partition = nullptr);
|
||||
void format(uuid::console::Shell & shell);
|
||||
void upload_status(bool in_progress);
|
||||
bool upload_status();
|
||||
|
||||
@@ -301,7 +301,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
if (command == "add") {
|
||||
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");
|
||||
ok = true;
|
||||
}
|
||||
@@ -342,13 +342,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
AsyncWebServerRequest request;
|
||||
request.method(HTTP_GET);
|
||||
request.url("/api/custom");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/custom/test_custom");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/custom/test_read_only");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/custom/test_ram");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
shell.invoke_command("call custom info");
|
||||
|
||||
#endif
|
||||
@@ -365,7 +365,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
AsyncWebServerRequest request;
|
||||
request.method(HTTP_GET);
|
||||
request.url("/api/scheduler");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
shell.invoke_command("call scheduler info");
|
||||
#endif
|
||||
ok = true;
|
||||
@@ -379,7 +379,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
AsyncWebServerRequest request;
|
||||
request.method(HTTP_GET);
|
||||
request.url("/api/boiler/coldshot");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
#else
|
||||
shell.invoke_command("call boiler coldshot");
|
||||
#endif
|
||||
@@ -777,11 +777,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
AsyncWebServerRequest request;
|
||||
request.method(HTTP_GET);
|
||||
request.url("/api/temperaturesensor/commands");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/temperaturesensor/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/temperaturesensor/01-0203-0405-0607");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
|
||||
ok = true;
|
||||
}
|
||||
@@ -827,12 +827,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
AsyncWebServerRequest request;
|
||||
request.method(HTTP_GET);
|
||||
request.url("/api/analogsensor/commands");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/analogsensor/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/analogsensor/test_analog1");
|
||||
request.url("/api/analogsensor/36");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
|
||||
// test renaming it
|
||||
// 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.url("/api/boiler/values");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/wwcirc");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/wwcirc/fullname");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/selburnpow/value");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/wwchargetype/writeable");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/flamecurr/value");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/flamecurr/bad");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -995,7 +995,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
deserializeJson(doc, odata);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/wwmode");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -1019,76 +1019,76 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
/*
|
||||
requestX.url("/api"); // should fail
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/thermostat/seltemp");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/thermostat/mode/auto");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/thermostat"); // check if defaults to info
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
requestX.url("/api/thermostat/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
requestX.url("/api/thermostat/values");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
|
||||
requestX.url("/api/thermostat/mode");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/system"); // check if defaults to info
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
emsesp::EMSESP::logger().notice("*");
|
||||
|
||||
requestX.url("/api/system/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
emsesp::EMSESP::logger().notice("*");
|
||||
|
||||
requestX.url("/api/thermostat"); // check if defaults to values
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
emsesp::EMSESP::logger().notice("*");
|
||||
|
||||
requestX.url("/api/thermostat/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
emsesp::EMSESP::logger().notice("*");
|
||||
|
||||
requestX.url("/api/thermostat/seltemp");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/system/restart");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/temperaturesensor/xxxx");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
emsesp::EMSESP::logger().notice("****");
|
||||
requestX.url("/api/temperaturesensor/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
return;
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api"); // should fail
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX);
|
||||
*/
|
||||
|
||||
requestX.method(HTTP_POST);
|
||||
@@ -1098,7 +1098,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
deserializeJson(docX, dataX);
|
||||
jsonX = docX.as<JsonVariant>();
|
||||
requestX.url("/api");
|
||||
EMSESP::webAPIService.webAPIService_post(&requestX, jsonX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX, jsonX);
|
||||
return;
|
||||
*/
|
||||
|
||||
@@ -1109,7 +1109,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
jsonX = docX.as<JsonVariant>();
|
||||
// requestX.url("/api/system/send");
|
||||
requestX.url("/api/thermostat");
|
||||
EMSESP::webAPIService.webAPIService_post(&requestX, jsonX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX, jsonX);
|
||||
return;
|
||||
*/
|
||||
|
||||
@@ -1118,7 +1118,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
deserializeJson(docX, dataX);
|
||||
jsonX = docX.as<JsonVariant>();
|
||||
requestX.url("/api/thermostat/mode/auto"); // should fail
|
||||
EMSESP::webAPIService.webAPIService_post(&requestX, jsonX);
|
||||
EMSESP::webAPIService.webAPIService(&requestX, jsonX);
|
||||
return;
|
||||
*/
|
||||
|
||||
@@ -1185,21 +1185,21 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
request.method(HTTP_GET);
|
||||
|
||||
request.url("/api/thermostat"); // check if defaults to info
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/thermostat/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/thermostat/values");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/thermostat/seltemp");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/system/commands");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/system/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/syspress");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/boiler/wwcurflow");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
|
||||
// POST tests
|
||||
request.method(HTTP_POST);
|
||||
@@ -1211,28 +1211,28 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
deserializeJson(doc, data1);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
|
||||
// 2
|
||||
char data2[] = "{\"value\":12}";
|
||||
deserializeJson(doc, data2);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/seltemp");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
|
||||
// 3
|
||||
char data3[] = "{\"device\":\"thermostat\", \"cmd\":\"seltemp\",\"value\":13}";
|
||||
deserializeJson(doc, data3);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
|
||||
// 4 - system call
|
||||
char data4[] = "{\"value\":\"0B 88 19 19 02\"}";
|
||||
deserializeJson(doc, data4);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/system/send");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
|
||||
// 5 - test write value
|
||||
// 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);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
|
||||
// write value from web - testing 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);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
|
||||
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);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/mode/auto");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
EMSESP::webAPIService.webAPIService(&request, json);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -1811,11 +1811,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// test API
|
||||
AsyncWebServerRequest request;
|
||||
request.url("/api/mixer");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/mixer/hc1/pumpstatus");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
request.url("/api/mixer/wwc2/pumpstatus");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
EMSESP::webAPIService.webAPIService(&request);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.6.5-test.16a"
|
||||
#define EMSESP_APP_VERSION "3.6.5-test.18"
|
||||
|
||||
@@ -24,12 +24,11 @@ uint32_t WebAPIService::api_count_ = 0;
|
||||
uint16_t WebAPIService::api_fails_ = 0;
|
||||
|
||||
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _apiHandler(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService_post(request, json); }) { // for POSTs
|
||||
server->on(EMSESP_API_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { webAPIService_get(request); }); // for GETs
|
||||
server->addHandler(&_apiHandler);
|
||||
: _securityManager(securityManager) {
|
||||
// API
|
||||
server->on(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService(request, json); });
|
||||
|
||||
// for settings
|
||||
// settings
|
||||
server->on(GET_SETTINGS_PATH,
|
||||
HTTP_GET,
|
||||
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));
|
||||
}
|
||||
|
||||
// HTTP GET
|
||||
// GET /{device}
|
||||
// GET /{device}/{entity}
|
||||
void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
|
||||
// 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) {
|
||||
// POST|GET /{device}
|
||||
// POST|GET /{device}/{entity}
|
||||
void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
JsonObject input;
|
||||
// if no body then treat it as a secure GET
|
||||
if (!json.is<JsonObject>()) {
|
||||
webAPIService_get(request);
|
||||
return;
|
||||
if ((request->method() == HTTP_GET) || (!json.is<JsonObject>())) {
|
||||
// HTTP GET
|
||||
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);
|
||||
}
|
||||
|
||||
#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
|
||||
// reporting back any errors
|
||||
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
|
||||
// api?device={device}&cmd={name}&data={value}&id={hc}
|
||||
if (request->url() == "/api") {
|
||||
if (request->url() == EMSESP_API_SERVICE_PATH) {
|
||||
// get the device
|
||||
if (request->hasParam(F_(device))) {
|
||||
input["device"] = request->getParam(F_(device))->value().c_str();
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define WebAPIService_h
|
||||
|
||||
#define EMSESP_API_SERVICE_PATH "/api"
|
||||
|
||||
#define GET_SETTINGS_PATH "/rest/getSettings"
|
||||
#define GET_CUSTOMIZATIONS_PATH "/rest/getCustomizations"
|
||||
#define GET_SCHEDULE_PATH "/rest/getSchedule"
|
||||
@@ -31,8 +32,12 @@ class WebAPIService {
|
||||
public:
|
||||
WebAPIService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant json); // for POSTs
|
||||
void webAPIService_get(AsyncWebServerRequest * request); // for GETs
|
||||
void webAPIService(AsyncWebServerRequest * request, JsonVariant json);
|
||||
|
||||
#ifdef EMSESP_TEST
|
||||
// for test.cpp
|
||||
void webAPIService(AsyncWebServerRequest * request);
|
||||
#endif
|
||||
|
||||
static uint32_t api_count() {
|
||||
return api_count_;
|
||||
@@ -44,13 +49,11 @@ class WebAPIService {
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
AsyncCallbackJsonWebHandler _apiHandler; // for POSTs
|
||||
|
||||
static uint32_t api_count_;
|
||||
static uint16_t api_fails_;
|
||||
|
||||
void parse(AsyncWebServerRequest * request, JsonObject input);
|
||||
|
||||
void getSettings(AsyncWebServerRequest * request);
|
||||
void getCustomizations(AsyncWebServerRequest * request);
|
||||
void getSchedule(AsyncWebServerRequest * request);
|
||||
|
||||
@@ -23,10 +23,7 @@ namespace emsesp {
|
||||
bool WebCustomization::_start = true;
|
||||
|
||||
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _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)) {
|
||||
: _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE) {
|
||||
server->on(DEVICE_ENTITIES_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { device_entities(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
@@ -39,9 +36,9 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { reset_customization(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
_masked_entities_handler.setMethod(HTTP_POST);
|
||||
_masked_entities_handler.setMaxContentLength(2048);
|
||||
server->addHandler(&_masked_entities_handler);
|
||||
server->on(CUSTOMIZATION_ENTITIES_PATH,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { customization_entities(request, json); },
|
||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
// this creates the customization file, saving it to the FS
|
||||
|
||||
@@ -101,8 +101,6 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
|
||||
// POST
|
||||
void customization_entities(AsyncWebServerRequest * request, JsonVariant json);
|
||||
void reset_customization(AsyncWebServerRequest * request); // command
|
||||
|
||||
AsyncCallbackJsonWebHandler _masked_entities_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -21,16 +21,18 @@
|
||||
namespace emsesp {
|
||||
|
||||
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); },
|
||||
AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request,
|
||||
JsonVariant json) { write_temperature_sensor(request, json); },
|
||||
AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH,
|
||||
AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_temperature_sensor(request, json); },
|
||||
AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(WRITE_ANALOG_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_analog_sensor(request, json); },
|
||||
AuthenticationPredicates::IS_ADMIN)) {
|
||||
AuthenticationPredicates::IS_ADMIN));
|
||||
// GET's
|
||||
server->on(DEVICE_DATA_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
@@ -49,19 +51,6 @@ WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securi
|
||||
server->on(SCAN_DEVICES_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
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
|
||||
|
||||
@@ -51,8 +51,6 @@ class WebDataService {
|
||||
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json);
|
||||
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json);
|
||||
void scan_devices(AsyncWebServerRequest * request); // command
|
||||
|
||||
AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -21,17 +21,14 @@
|
||||
namespace emsesp {
|
||||
|
||||
WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: events_(EVENT_SOURCE_LOG_PATH)
|
||||
, setValues_(LOG_SETTINGS_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { setValues(request, json); }) {
|
||||
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
// get settings
|
||||
server->on(LOG_SETTINGS_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { getValues(request); });
|
||||
: events_(EVENT_SOURCE_LOG_PATH) {
|
||||
// get & set settings
|
||||
server->on(LOG_SETTINGS_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { getSetValues(request, json); });
|
||||
|
||||
// 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->addHandler(&setValues_);
|
||||
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
|
||||
server->addHandler(&events_);
|
||||
}
|
||||
|
||||
@@ -204,11 +201,20 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
// sets the values like level after a POST
|
||||
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
if (!json.is<JsonObject>()) {
|
||||
void WebLogService::getSetValues(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
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;
|
||||
}
|
||||
|
||||
// POST - write the settings
|
||||
auto && body = json.as<JsonObject>();
|
||||
|
||||
uuid::log::Level level = body["level"];
|
||||
@@ -223,15 +229,4 @@ void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant json)
|
||||
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
|
||||
@@ -60,14 +60,10 @@ class WebLogService : public uuid::log::Handler {
|
||||
|
||||
void transmit(const QueuedLogMessage & message);
|
||||
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);
|
||||
|
||||
void setValues(AsyncWebServerRequest * request, JsonVariant json);
|
||||
|
||||
AsyncCallbackJsonWebHandler setValues_; // for POSTs
|
||||
|
||||
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 limit_log_messages_ = 1; // dynamic limit
|
||||
|
||||
Reference in New Issue
Block a user