mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-10 18:24:27 +05:00
* feat: new portal & network manager
* refactor: migrate from PubSubClient to ArduinoMqttClient * refactor: migrate from EEManager to FileData * chore: bump ESP Telnet to 2.2 * chore: bump TinyLogger to 1.1.0
This commit is contained in:
82
lib/BufferedWebServer/BufferedWebServer.h
Normal file
82
lib/BufferedWebServer/BufferedWebServer.h
Normal file
@@ -0,0 +1,82 @@
|
||||
class BufferedWebServer {
|
||||
public:
|
||||
BufferedWebServer(WebServer* webServer, size_t bufferSize = 64) {
|
||||
this->webServer = webServer;
|
||||
this->bufferSize = bufferSize;
|
||||
this->buffer = (uint8_t*)malloc(bufferSize * sizeof(*this->buffer));
|
||||
}
|
||||
|
||||
~BufferedWebServer() {
|
||||
free(this->buffer);
|
||||
}
|
||||
|
||||
void send(int code, const char* contentType, JsonDocument& content) {
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (!this->webServer->chunkedResponseModeStart(code, contentType)) {
|
||||
this->webServer->send(505, F("text/html"), F("HTTP1.1 required"));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
this->webServer->send(code, contentType, "");
|
||||
#endif
|
||||
|
||||
this->webServer->setContentLength(measureJson(content));
|
||||
serializeJson(content, *this);
|
||||
this->flush();
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
this->webServer->chunkedResponseFinalize();
|
||||
#else
|
||||
this->webServer->sendContent("");
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
this->buffer[this->bufferPos++] = c;
|
||||
|
||||
if (this->bufferPos >= this->bufferSize) {
|
||||
this->flush();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t write(const uint8_t* buffer, size_t length) {
|
||||
size_t written = 0;
|
||||
while (written < length) {
|
||||
size_t copySize = this->bufferSize - this->bufferPos;
|
||||
if (written + copySize > length) {
|
||||
copySize = length - written;
|
||||
}
|
||||
|
||||
memcpy(this->buffer + this->bufferPos, buffer + written, copySize);
|
||||
this->bufferPos += copySize;
|
||||
|
||||
if (this->bufferPos >= this->bufferSize) {
|
||||
this->flush();
|
||||
}
|
||||
|
||||
written += copySize;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (this->bufferPos == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->webServer->sendContent((const char*)this->buffer, this->bufferPos);
|
||||
this->bufferPos = 0;
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
::yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
WebServer* webServer = nullptr;
|
||||
uint8_t* buffer;
|
||||
size_t bufferSize = 64;
|
||||
size_t bufferPos = 0;
|
||||
};
|
||||
129
lib/Connection/Connection.cpp
Normal file
129
lib/Connection/Connection.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "Connection.h"
|
||||
|
||||
void Connection::setup(bool useDhcp) {
|
||||
setUseDhcp(useDhcp);
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
wifi_set_event_handler_cb(Connection::onEvent);
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
WiFi.onEvent(Connection::onEvent);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Connection::setUseDhcp(bool value) {
|
||||
useDhcp = value;
|
||||
}
|
||||
|
||||
Connection::Status Connection::getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
Connection::DisconnectReason Connection::getDisconnectReason() {
|
||||
return disconnectReason;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
void Connection::onEvent(System_Event_t *evt) {
|
||||
switch (evt->event) {
|
||||
case EVENT_STAMODE_CONNECTED:
|
||||
status = useDhcp ? Status::CONNECTING : Status::CONNECTED;
|
||||
disconnectReason = DisconnectReason::NONE;
|
||||
|
||||
break;
|
||||
|
||||
case EVENT_STAMODE_GOT_IP:
|
||||
status = Status::CONNECTED;
|
||||
disconnectReason = DisconnectReason::NONE;
|
||||
break;
|
||||
|
||||
case EVENT_STAMODE_DHCP_TIMEOUT:
|
||||
status = Status::DISCONNECTED;
|
||||
disconnectReason = DisconnectReason::DHCP_TIMEOUT;
|
||||
break;
|
||||
|
||||
case EVENT_STAMODE_DISCONNECTED:
|
||||
status = Status::DISCONNECTED;
|
||||
disconnectReason = convertDisconnectReason(evt->event_info.disconnected.reason);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
void Connection::onEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||
status = useDhcp ? Status::CONNECTING : Status::CONNECTED;
|
||||
disconnectReason = DisconnectReason::NONE;
|
||||
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
||||
status = Status::CONNECTED;
|
||||
disconnectReason = DisconnectReason::NONE;
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
|
||||
status = Status::DISCONNECTED;
|
||||
disconnectReason = DisconnectReason::DHCP_TIMEOUT;
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
status = Status::DISCONNECTED;
|
||||
disconnectReason = convertDisconnectReason(info.wifi_sta_disconnected.reason);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//Serial.printf("SYS EVENT: %d, reason: %d\n\r", evt->event, disconnectReason);
|
||||
}
|
||||
#endif
|
||||
|
||||
Connection::DisconnectReason Connection::convertDisconnectReason(uint8_t reason) {
|
||||
switch (reason) {
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
case REASON_BEACON_TIMEOUT:
|
||||
return DisconnectReason::BEACON_TIMEOUT;
|
||||
|
||||
case REASON_NO_AP_FOUND:
|
||||
return DisconnectReason::NO_AP_FOUND;
|
||||
|
||||
case REASON_AUTH_FAIL:
|
||||
return DisconnectReason::AUTH_FAIL;
|
||||
|
||||
case REASON_ASSOC_FAIL:
|
||||
return DisconnectReason::ASSOC_FAIL;
|
||||
|
||||
case REASON_HANDSHAKE_TIMEOUT:
|
||||
return DisconnectReason::HANDSHAKE_TIMEOUT;
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
case WIFI_REASON_BEACON_TIMEOUT:
|
||||
return DisconnectReason::BEACON_TIMEOUT;
|
||||
|
||||
case WIFI_REASON_NO_AP_FOUND:
|
||||
return DisconnectReason::NO_AP_FOUND;
|
||||
|
||||
case WIFI_REASON_AUTH_FAIL:
|
||||
return DisconnectReason::AUTH_FAIL;
|
||||
|
||||
case WIFI_REASON_ASSOC_FAIL:
|
||||
return DisconnectReason::ASSOC_FAIL;
|
||||
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT:
|
||||
return DisconnectReason::HANDSHAKE_TIMEOUT;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return DisconnectReason::OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
bool Connection::useDhcp = false;
|
||||
Connection::Status Connection::status = Status::DISCONNECTED;
|
||||
Connection::DisconnectReason Connection::disconnectReason = DisconnectReason::NONE;
|
||||
43
lib/Connection/Connection.h
Normal file
43
lib/Connection/Connection.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "lwip/etharp.h"
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
struct Connection {
|
||||
enum class Status {
|
||||
CONNECTED,
|
||||
CONNECTING,
|
||||
GOT_IP,
|
||||
DISCONNECTED
|
||||
};
|
||||
|
||||
enum class DisconnectReason {
|
||||
BEACON_TIMEOUT,
|
||||
NO_AP_FOUND,
|
||||
AUTH_FAIL,
|
||||
ASSOC_FAIL,
|
||||
HANDSHAKE_TIMEOUT,
|
||||
DHCP_TIMEOUT,
|
||||
OTHER,
|
||||
NONE
|
||||
};
|
||||
|
||||
static Status status;
|
||||
static DisconnectReason disconnectReason;
|
||||
|
||||
static void setup(bool useDhcp);
|
||||
static void setUseDhcp(bool value);
|
||||
static Status getStatus();
|
||||
static DisconnectReason getDisconnectReason();
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
static void onEvent(System_Event_t *evt);
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
static void onEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static DisconnectReason convertDisconnectReason(uint8_t reason);
|
||||
static bool useDhcp;
|
||||
};
|
||||
@@ -3,6 +3,15 @@
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
const char HA_ENTITY_BINARY_SENSOR[] PROGMEM = "binary_sensor";
|
||||
const char HA_ENTITY_BUTTON[] PROGMEM = "button";
|
||||
const char HA_ENTITY_FAN[] PROGMEM = "fan";
|
||||
const char HA_ENTITY_CLIMATE[] PROGMEM = "climate";
|
||||
const char HA_ENTITY_NUMBER[] PROGMEM = "number";
|
||||
const char HA_ENTITY_SELECT[] PROGMEM = "select";
|
||||
const char HA_ENTITY_SENSOR[] PROGMEM = "sensor";
|
||||
const char HA_ENTITY_SWITCH[] PROGMEM = "switch";
|
||||
|
||||
const char HA_DEVICE[] PROGMEM = "device";
|
||||
const char HA_IDENTIFIERS[] PROGMEM = "identifiers";
|
||||
const char HA_SW_VERSION[] PROGMEM = "sw_version";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <MqttClient.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <mutex>
|
||||
#endif
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
class MqttWriter {
|
||||
public:
|
||||
MqttWriter(PubSubClient* client, size_t bufferSize = 64) {
|
||||
MqttWriter(MqttClient* client, size_t bufferSize = 64) {
|
||||
this->client = client;
|
||||
this->bufferSize = bufferSize;
|
||||
this->buffer = (uint8_t*) malloc(bufferSize * sizeof(*this->buffer));
|
||||
@@ -94,9 +94,10 @@ public:
|
||||
this->bufferPos = 0;
|
||||
size_t docSize = measureJson(doc);
|
||||
size_t written = 0;
|
||||
if (this->client->beginPublish(topic, docSize, retained)) {
|
||||
if (this->client->beginMessage(topic, docSize, retained)) {
|
||||
serializeJson(doc, *this);
|
||||
this->flush();
|
||||
this->client->endMessage();
|
||||
|
||||
written = this->writeAfterLock;
|
||||
}
|
||||
@@ -110,7 +111,7 @@ public:
|
||||
}
|
||||
|
||||
bool publish(const char* topic, const char* buffer, bool retained = false) {
|
||||
return this->publish(topic, (uint8_t*) buffer, strlen(buffer), retained);
|
||||
return this->publish(topic, (const uint8_t*) buffer, strlen(buffer), retained);
|
||||
}
|
||||
|
||||
bool publish(const char* topic, const uint8_t* buffer, size_t length, bool retained = false) {
|
||||
@@ -128,12 +129,13 @@ public:
|
||||
this->bufferPos = 0;
|
||||
size_t written = 0;
|
||||
bool result = false;
|
||||
if (length == 0) {
|
||||
result = this->client->publish(topic, nullptr, 0, retained);
|
||||
if (!length || buffer == nullptr) {
|
||||
result = this->client->beginMessage(topic, 0, retained) && this->client->endMessage();
|
||||
|
||||
} else if (this->client->beginPublish(topic, length, retained)) {
|
||||
} else if (this->client->beginMessage(topic, length, retained)) {
|
||||
this->write(buffer, length);
|
||||
this->flush();
|
||||
this->client->endMessage();
|
||||
|
||||
written = this->writeAfterLock;
|
||||
result = written == length;
|
||||
@@ -204,7 +206,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
PubSubClient* client;
|
||||
MqttClient* client;
|
||||
uint8_t* buffer;
|
||||
size_t bufferSize = 64;
|
||||
size_t bufferPos = 0;
|
||||
|
||||
227
lib/WebServerHandlers/DynamicPage.h
Normal file
227
lib/WebServerHandlers/DynamicPage.h
Normal file
@@ -0,0 +1,227 @@
|
||||
#include <FS.h>
|
||||
|
||||
|
||||
class DynamicPage : public RequestHandler {
|
||||
public:
|
||||
typedef std::function<bool(HTTPMethod, const String&)> canHandleFunction;
|
||||
typedef std::function<bool()> beforeSendFunction;
|
||||
typedef std::function<String(const char*)> templateFunction;
|
||||
|
||||
DynamicPage(const char* uri, FS* fs, const char* path, const char* cacheHeader = nullptr) {
|
||||
this->uri = uri;
|
||||
this->fs = fs;
|
||||
this->path = path;
|
||||
this->cacheHeader = cacheHeader;
|
||||
}
|
||||
|
||||
DynamicPage* setCanHandleFunction(canHandleFunction val = nullptr) {
|
||||
this->canHandleFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DynamicPage* setBeforeSendFunction(beforeSendFunction val = nullptr) {
|
||||
this->beforeSendFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DynamicPage* setTemplateFunction(templateFunction val = nullptr) {
|
||||
this->templateFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
bool canHandle(HTTPMethod method, const String uri) override {
|
||||
#else
|
||||
bool canHandle(HTTPMethod method, const String& uri) override {
|
||||
#endif
|
||||
return uri.equals(this->uri) && (!this->canHandleFn || this->canHandleFn(method, uri));
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
bool handle(WebServer& server, HTTPMethod method, const String uri) override {
|
||||
#else
|
||||
bool handle(WebServer& server, HTTPMethod method, const String& uri) override {
|
||||
#endif
|
||||
if (!this->canHandle(method, uri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->beforeSendFn && !this->beforeSendFn()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
File file = this->fs->open(this->path, "r");
|
||||
if (!file) {
|
||||
return false;
|
||||
|
||||
} else if (file.isDirectory()) {
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->cacheHeader != nullptr) {
|
||||
server.sendHeader("Cache-Control", this->cacheHeader);
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
if (!server.chunkedResponseModeStart(200, F("text/html"))) {
|
||||
server.send(505, F("text/html"), F("HTTP1.1 required"));
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
server.send(200, F("text/html"), "");
|
||||
#endif
|
||||
|
||||
uint8_t* argStartPos = nullptr;
|
||||
uint8_t* argEndPos = nullptr;
|
||||
uint8_t argName[16];
|
||||
size_t sizeArgName = 0;
|
||||
bool argNameProcess = false;
|
||||
while (file.available()) {
|
||||
uint8_t buf[64];
|
||||
size_t length = file.read(buf, sizeof(buf));
|
||||
size_t offset = 0;
|
||||
|
||||
if (argNameProcess) {
|
||||
argEndPos = (uint8_t*) memchr(buf, '}', length);
|
||||
|
||||
if (argEndPos != nullptr) {
|
||||
size_t fullSizeArgName = sizeArgName + (argEndPos - buf);
|
||||
if (fullSizeArgName < sizeof(argName)) {
|
||||
// copy full arg name
|
||||
if (argEndPos - buf > 0) {
|
||||
memcpy(argName + sizeArgName, buf, argEndPos - buf);
|
||||
}
|
||||
argName[fullSizeArgName] = '\0';
|
||||
|
||||
// send arg value
|
||||
String argValue = this->templateFn((const char*) argName);
|
||||
if (argValue.length()) {
|
||||
server.sendContent(argValue.c_str());
|
||||
|
||||
} else if (fullSizeArgName > 0) {
|
||||
server.sendContent("{");
|
||||
server.sendContent((const char*) argName);
|
||||
server.sendContent("}");
|
||||
}
|
||||
|
||||
offset = size_t(argEndPos - buf + 1);
|
||||
sizeArgName = 0;
|
||||
argNameProcess = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (argNameProcess) {
|
||||
server.sendContent("{");
|
||||
|
||||
if (sizeArgName > 0) {
|
||||
argName[sizeArgName] = '\0';
|
||||
server.sendContent((const char*) argName);
|
||||
}
|
||||
|
||||
argNameProcess = false;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
uint8_t* currentBuf = buf + offset;
|
||||
size_t currentLength = length - offset;
|
||||
|
||||
argStartPos = (uint8_t*) memchr(currentBuf, '{', currentLength);
|
||||
|
||||
// send all content
|
||||
if (argStartPos == nullptr) {
|
||||
if (currentLength > 0) {
|
||||
server.sendContent((const char*) currentBuf, currentLength);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
argEndPos = (uint8_t*) memchr(argStartPos, '}', length - (argStartPos - buf));
|
||||
if (argEndPos != nullptr) {
|
||||
sizeArgName = argEndPos - argStartPos - 1;
|
||||
|
||||
// send all content if arg len > space
|
||||
if (sizeArgName >= sizeof(argName)) {
|
||||
if (currentLength > 0) {
|
||||
server.sendContent((const char*) currentBuf, currentLength);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// arg name
|
||||
memcpy(argName, argStartPos + 1, sizeArgName);
|
||||
argName[sizeArgName] = '\0';
|
||||
|
||||
// send arg value
|
||||
String argValue = this->templateFn((const char*) argName);
|
||||
if (argValue.length()) {
|
||||
// send content before var
|
||||
if (argStartPos - buf > 0) {
|
||||
server.sendContent((const char*) currentBuf, argStartPos - buf);
|
||||
}
|
||||
|
||||
server.sendContent(argValue.c_str());
|
||||
|
||||
} else {
|
||||
server.sendContent((const char*) currentBuf, argEndPos - currentBuf + 1);
|
||||
}
|
||||
|
||||
offset = size_t(argEndPos - currentBuf + 1);
|
||||
|
||||
} else {
|
||||
sizeArgName = length - size_t(argStartPos - currentBuf) - 1;
|
||||
Serial.printf("sizeArgName: %d\r\n", sizeArgName);
|
||||
|
||||
// send all content if arg len > space
|
||||
if (sizeArgName >= sizeof(argName)) {
|
||||
if (currentLength) {
|
||||
server.sendContent((const char*) currentBuf, currentLength);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// send content before var
|
||||
if (argStartPos - buf > 0) {
|
||||
server.sendContent((const char*) currentBuf, argStartPos - buf);
|
||||
}
|
||||
|
||||
// copy arg name chunk
|
||||
if (sizeArgName > 0) {
|
||||
memcpy(argName, argStartPos + 1, sizeArgName);
|
||||
}
|
||||
|
||||
argNameProcess = true;
|
||||
|
||||
break;
|
||||
}
|
||||
} while(true);
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
server.chunkedResponseFinalize();
|
||||
#else
|
||||
server.sendContent("");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
FS* fs = nullptr;
|
||||
canHandleFunction canHandleFn;
|
||||
beforeSendFunction beforeSendFn;
|
||||
templateFunction templateFn;
|
||||
String eTag;
|
||||
const char* uri = nullptr;
|
||||
const char* path = nullptr;
|
||||
const char* cacheHeader = nullptr;
|
||||
};
|
||||
96
lib/WebServerHandlers/StaticPage.h
Normal file
96
lib/WebServerHandlers/StaticPage.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#include <FS.h>
|
||||
|
||||
class StaticPage : public RequestHandler {
|
||||
public:
|
||||
typedef std::function<bool(HTTPMethod, const String&)> canHandleFunction;
|
||||
typedef std::function<bool()> beforeSendFunction;
|
||||
|
||||
StaticPage(const char* uri, FS* fs, const char* path, const char* cacheHeader = nullptr) {
|
||||
this->uri = uri;
|
||||
this->fs = fs;
|
||||
this->path = path;
|
||||
this->cacheHeader = cacheHeader;
|
||||
}
|
||||
|
||||
StaticPage* setCanHandleFunction(canHandleFunction val = nullptr) {
|
||||
this->canHandleFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
StaticPage* setBeforeSendFunction(beforeSendFunction val = nullptr) {
|
||||
this->beforeSendFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
bool canHandle(HTTPMethod method, const String uri) override {
|
||||
#else
|
||||
bool canHandle(HTTPMethod method, const String& uri) override {
|
||||
#endif
|
||||
return method == HTTP_GET && uri.equals(this->uri) && (!this->canHandleFn || this->canHandleFn(method, uri));
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
bool handle(WebServer& server, HTTPMethod method, const String uri) override {
|
||||
#else
|
||||
bool handle(WebServer& server, HTTPMethod method, const String& uri) override {
|
||||
#endif
|
||||
if (!this->canHandle(method, uri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->beforeSendFn && !this->beforeSendFn()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
if (server._eTagEnabled) {
|
||||
if (server._eTagFunction) {
|
||||
this->eTag = (server._eTagFunction)(*this->fs, this->path);
|
||||
|
||||
} else if (this->eTag.isEmpty()) {
|
||||
this->eTag = esp8266webserver::calcETag(*this->fs, this->path);
|
||||
}
|
||||
|
||||
if (server.header("If-None-Match").equals(this->eTag.c_str())) {
|
||||
server.send(304);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
File file = this->fs->open(this->path, "r");
|
||||
if (!file) {
|
||||
return false;
|
||||
|
||||
} else if (file.isDirectory()) {
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->cacheHeader != nullptr) {
|
||||
server.sendHeader("Cache-Control", this->cacheHeader);
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
if (server._eTagEnabled && this->eTag.length() > 0) {
|
||||
server.sendHeader("ETag", this->eTag);
|
||||
}
|
||||
#endif
|
||||
|
||||
server.streamFile(file, F("text/html"), method);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
FS* fs = nullptr;
|
||||
canHandleFunction canHandleFn;
|
||||
beforeSendFunction beforeSendFn;
|
||||
String eTag;
|
||||
const char* uri = nullptr;
|
||||
const char* path = nullptr;
|
||||
const char* cacheHeader = nullptr;
|
||||
};
|
||||
218
lib/WebServerHandlers/UpgradeHandler.h
Normal file
218
lib/WebServerHandlers/UpgradeHandler.h
Normal file
@@ -0,0 +1,218 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
class UpgradeHandler : public RequestHandler {
|
||||
public:
|
||||
enum class UpgradeType {
|
||||
FIRMWARE = 0,
|
||||
FILESYSTEM = 1
|
||||
};
|
||||
|
||||
enum class UpgradeStatus {
|
||||
NONE,
|
||||
NO_FILE,
|
||||
SUCCESS,
|
||||
PROHIBITED,
|
||||
ABORTED,
|
||||
ERROR_ON_START,
|
||||
ERROR_ON_WRITE,
|
||||
ERROR_ON_FINISH
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
UpgradeType type;
|
||||
UpgradeStatus status;
|
||||
String error;
|
||||
} UpgradeResult;
|
||||
|
||||
typedef std::function<bool(HTTPMethod, const String&)> CanHandleFunction;
|
||||
typedef std::function<bool(const String&)> CanUploadFunction;
|
||||
typedef std::function<bool(UpgradeType)> BeforeUpgradeFunction;
|
||||
typedef std::function<void(const UpgradeResult&, const UpgradeResult&)> AfterUpgradeFunction;
|
||||
|
||||
UpgradeHandler(const char* uri) {
|
||||
this->uri = uri;
|
||||
}
|
||||
|
||||
UpgradeHandler* setCanHandleFunction(CanHandleFunction val = nullptr) {
|
||||
this->canHandleFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
UpgradeHandler* setCanUploadFunction(CanUploadFunction val = nullptr) {
|
||||
this->canUploadFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
UpgradeHandler* setBeforeUpgradeFunction(BeforeUpgradeFunction val = nullptr) {
|
||||
this->beforeUpgradeFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
UpgradeHandler* setAfterUpgradeFunction(AfterUpgradeFunction val = nullptr) {
|
||||
this->afterUpgradeFn = val;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
bool canHandle(HTTPMethod method, const String uri) override {
|
||||
#else
|
||||
bool canHandle(HTTPMethod method, const String& uri) override {
|
||||
#endif
|
||||
return method == HTTP_POST && uri.equals(this->uri) && (!this->canHandleFn || this->canHandleFn(method, uri));
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
bool canUpload(const String uri) override {
|
||||
#else
|
||||
bool canUpload(const String& uri) override {
|
||||
#endif
|
||||
return uri.equals(this->uri) && (!this->canUploadFn || this->canUploadFn(uri));
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
bool handle(WebServer& server, HTTPMethod method, const String uri) override {
|
||||
#else
|
||||
bool handle(WebServer& server, HTTPMethod method, const String& uri) override {
|
||||
#endif
|
||||
if (this->afterUpgradeFn) {
|
||||
this->afterUpgradeFn(this->firmwareResult, this->filesystemResult);
|
||||
}
|
||||
|
||||
this->firmwareResult.status = UpgradeStatus::NONE;
|
||||
this->firmwareResult.error.clear();
|
||||
|
||||
this->filesystemResult.status = UpgradeStatus::NONE;
|
||||
this->filesystemResult.error.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void upload(WebServer& server, const String uri, HTTPUpload& upload) override {
|
||||
#else
|
||||
void upload(WebServer& server, const String& uri, HTTPUpload& upload) override {
|
||||
#endif
|
||||
UpgradeResult* result;
|
||||
if (upload.name.equals("firmware")) {
|
||||
result = &this->firmwareResult;
|
||||
} else if (upload.name.equals("filesystem")) {
|
||||
result = &this->filesystemResult;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->status != UpgradeStatus::NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->beforeUpgradeFn && !this->beforeUpgradeFn(result->type)) {
|
||||
result->status = UpgradeStatus::PROHIBITED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!upload.filename.length()) {
|
||||
result->status = UpgradeStatus::NO_FILE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
// reset
|
||||
if (Update.isRunning()) {
|
||||
Update.end(false);
|
||||
Update.clearError();
|
||||
}
|
||||
|
||||
bool begin = false;
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
Update.runAsync(true);
|
||||
|
||||
if (result->type == UpgradeType::FIRMWARE) {
|
||||
begin = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000, U_FLASH);
|
||||
|
||||
} else if (result->type == UpgradeType::FILESYSTEM) {
|
||||
close_all_fs();
|
||||
begin = Update.begin((size_t)FS_end - (size_t)FS_start, U_FS);
|
||||
}
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
if (result->type == UpgradeType::FIRMWARE) {
|
||||
begin = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH);
|
||||
|
||||
} else if (result->type == UpgradeType::FILESYSTEM) {
|
||||
begin = Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!begin || Update.hasError()) {
|
||||
result->status = UpgradeStatus::ERROR_ON_START;
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
result->error = Update.getErrorString();
|
||||
#else
|
||||
result->error = Update.errorString();
|
||||
#endif
|
||||
|
||||
Log.serrorln("PORTAL.OTA", F("File '%s', on start: %s"), upload.filename.c_str(), result->error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Log.sinfoln("PORTAL.OTA", F("File '%s', started"), upload.filename.c_str());
|
||||
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
Update.end(false);
|
||||
|
||||
result->status = UpgradeStatus::ERROR_ON_WRITE;
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
result->error = Update.getErrorString();
|
||||
#else
|
||||
result->error = Update.errorString();
|
||||
#endif
|
||||
|
||||
Log.serrorln(
|
||||
"PORTAL.OTA",
|
||||
F("File '%s', on writing %d bytes: %s"),
|
||||
upload.filename.c_str(), upload.totalSize, result->error
|
||||
);
|
||||
|
||||
} else {
|
||||
Log.sinfoln("PORTAL.OTA", F("File '%s', writed %d bytes"), upload.filename.c_str(), upload.totalSize);
|
||||
}
|
||||
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (Update.end(true)) {
|
||||
result->status = UpgradeStatus::SUCCESS;
|
||||
|
||||
Log.sinfoln("PORTAL.OTA", F("File '%s': finish"), upload.filename.c_str());
|
||||
|
||||
} else {
|
||||
result->status = UpgradeStatus::ERROR_ON_FINISH;
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
result->error = Update.getErrorString();
|
||||
#else
|
||||
result->error = Update.errorString();
|
||||
#endif
|
||||
|
||||
Log.serrorln("PORTAL.OTA", F("File '%s', on finish: %s"), upload.filename.c_str(), result->error);
|
||||
}
|
||||
|
||||
} else if (upload.status == UPLOAD_FILE_ABORTED) {
|
||||
Update.end(false);
|
||||
result->status = UpgradeStatus::ABORTED;
|
||||
|
||||
Log.serrorln("PORTAL.OTA", F("File '%s': aborted"), upload.filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
CanHandleFunction canHandleFn;
|
||||
CanUploadFunction canUploadFn;
|
||||
BeforeUpgradeFunction beforeUpgradeFn;
|
||||
AfterUpgradeFunction afterUpgradeFn;
|
||||
const char* uri = nullptr;
|
||||
|
||||
UpgradeResult firmwareResult{UpgradeType::FIRMWARE, UpgradeStatus::NONE};
|
||||
UpgradeResult filesystemResult{UpgradeType::FILESYSTEM, UpgradeStatus::NONE};
|
||||
};
|
||||
Reference in New Issue
Block a user