From 63e75c2d63bc69da30fdb72517722869a8306d36 Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 20 Jul 2020 15:54:57 +0200 Subject: [PATCH] standalone w/ web server for testing --- lib_standalone/Arduino.cpp | 1 + lib_standalone/Arduino.h | 21 +- lib_standalone/AsyncJson.h | 163 +++++ lib_standalone/AsyncMqttClient.h | 143 +++++ lib_standalone/AsyncTCP.h | 212 +++++++ lib_standalone/ESP8266React.h | 94 ++- lib_standalone/ESPAsyncWebServer.h | 521 ++++++++++++++++ lib_standalone/FS.h | 10 + lib_standalone/FSPersistence.h | 102 +++ lib_standalone/Features.h | 37 ++ lib_standalone/HttpEndpoint.h | 173 ++++++ lib_standalone/SecurityManager.h | 102 +++ lib_standalone/StatefulService.cpp | 3 + lib_standalone/StatefulService.h | 144 +++++ lib_standalone/WString.cpp | 863 ++++++++++++++++++++++++++ lib_standalone/WString.h | 236 +++++++ lib_standalone/emsuart_standalone.cpp | 4 - makefile | 3 +- src/console.cpp | 4 +- src/console.h | 4 - src/devices/solar.cpp | 4 - src/emsesp.cpp | 36 +- src/emsesp.h | 2 +- src/mqtt.cpp | 29 +- src/mqtt.h | 12 - src/system.cpp | 10 +- src/test/test.h | 2 + 27 files changed, 2847 insertions(+), 88 deletions(-) create mode 100644 lib_standalone/AsyncMqttClient.h create mode 100644 lib_standalone/AsyncTCP.h create mode 100644 lib_standalone/FS.h create mode 100644 lib_standalone/Features.h create mode 100644 lib_standalone/StatefulService.cpp create mode 100644 lib_standalone/StatefulService.h create mode 100644 lib_standalone/WString.cpp create mode 100644 lib_standalone/WString.h diff --git a/lib_standalone/Arduino.cpp b/lib_standalone/Arduino.cpp index f67457fc5..b2bf854ac 100644 --- a/lib_standalone/Arduino.cpp +++ b/lib_standalone/Arduino.cpp @@ -39,6 +39,7 @@ static int __output_level[256]; int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) { memset(__output_pins, 0, sizeof(__output_pins)); memset(__output_level, 0, sizeof(__output_level)); + setup(); // loop(); // run once diff --git a/lib_standalone/Arduino.h b/lib_standalone/Arduino.h index 794d88c7d..459fba0d8 100644 --- a/lib_standalone/Arduino.h +++ b/lib_standalone/Arduino.h @@ -26,12 +26,15 @@ #include #include // for count_if +#include + #define IPAddress std::string #define ICACHE_FLASH_ATTR #define ICACHE_RAM_ATTR #define os_event_t void #define byte uint8_t #define ltoa itoa +#define boolean bool #define LOW 0 #define HIGH 1 @@ -54,14 +57,11 @@ int digitalRead(uint8_t pin); &__c[0]; \ })) + class __FlashStringHelper; #define FPSTR(string_literal) (reinterpret_cast(string_literal)) #define F(string_literal) (FPSTR(PSTR(string_literal))) -#define strlen_P strlen -#define strncpy_P strncpy -#define strcmp_P strcmp - int snprintf_P(char * str, size_t size, const char * format, ...); int vsnprintf_P(char * str, size_t size, const char * format, va_list ap); @@ -136,6 +136,7 @@ class Stream : public Print { virtual int peek() = 0; }; +/* class String { public: String(const char * data = "") @@ -150,9 +151,18 @@ class String { return data_.c_str(); } + bool equals(String comp) { + return (data_ == comp.c_str()); + } + + bool isEmpty() { + return data_.empty(); + } + private: std::string data_; }; +*/ class NativeConsole : public Stream { public: @@ -215,7 +225,10 @@ void yield(void); void setup(void); void loop(void); + size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize); size_t strlcat(char * dst, const char * src, size_t siz); +#include "WString.h" + #endif diff --git a/lib_standalone/AsyncJson.h b/lib_standalone/AsyncJson.h index e69de29bb..e9a36ba76 100644 --- a/lib_standalone/AsyncJson.h +++ b/lib_standalone/AsyncJson.h @@ -0,0 +1,163 @@ + +#ifndef ASYNC_JSON_H_ +#define ASYNC_JSON_H_ +#include +#include +// #include + +#define DYNAMIC_JSON_DOCUMENT_SIZE 1024 + +constexpr const char * JSON_MIMETYPE = "application/json"; + +/* + * Json Response + * */ + +class ChunkPrint : public Print { + private: + uint8_t * _destination; + size_t _to_skip; + size_t _to_write; + size_t _pos; + + public: + ChunkPrint(uint8_t * destination, size_t from, size_t len) + : _destination(destination) + , _to_skip(from) + , _to_write(len) + , _pos{0} { + } + virtual ~ChunkPrint() { + } + size_t write(uint8_t c) { + if (_to_skip > 0) { + _to_skip--; + return 1; + } else if (_to_write > 0) { + _to_write--; + _destination[_pos++] = c; + return 1; + } + return 0; + } + size_t write(const uint8_t * buffer, size_t size) { + return this->Print::write(buffer, size); + } +}; + +class AsyncJsonResponse { + protected: + + DynamicJsonDocument _jsonBuffer; + + JsonVariant _root; + bool _isValid; + + public: + AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) + : _jsonBuffer(maxJsonBufferSize) + , _isValid{false} { + if (isArray) + _root = _jsonBuffer.createNestedArray(); + else + _root = _jsonBuffer.createNestedObject(); + } + ~AsyncJsonResponse() { + } + JsonVariant & getRoot() { + return _root; + } + bool _sourceValid() const { + return _isValid; + } + size_t setLength() { +return 0; + } + + size_t getSize() { + return _jsonBuffer.size(); + } + + size_t _fillBuffer(uint8_t * data, size_t len) { + // ChunkPrint dest(data, 0, len); + + // serializeJson(_root, dest); + return len; + } +}; + +typedef std::function ArJsonRequestHandlerFunction; + +class AsyncCallbackJsonWebHandler : public AsyncWebHandler { + private: + protected: + const String _uri; + WebRequestMethodComposite _method; + ArJsonRequestHandlerFunction _onRequest; + size_t _contentLength; + size_t _maxContentLength; + + public: + AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) + : _uri(uri) + , _method(HTTP_POST | HTTP_PUT | HTTP_PATCH) + , _onRequest(onRequest) + , _maxContentLength(16384) { + } + + void setMethod(WebRequestMethodComposite method) { + _method = method; + } + void setMaxContentLength(int maxContentLength) { + _maxContentLength = maxContentLength; + } + void onRequest(ArJsonRequestHandlerFunction fn) { + _onRequest = fn; + } + + virtual bool canHandle(AsyncWebServerRequest * request) override final { + if (!_onRequest) + return false; + + if (!(_method & request->method())) + return false; + + request->addInterestingHeader("ANY"); + return true; + } + + virtual void handleRequest(AsyncWebServerRequest * request) override final { + if (_onRequest) { + if (request->_tempObject != NULL) { + DynamicJsonDocument jsonBuffer(1024); + DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); + if (!error) { + JsonVariant json = jsonBuffer.as(); + + _onRequest(request, json); + return; + } + } + request->send(_contentLength > _maxContentLength ? 413 : 400); + } else { + request->send(500); + } + } + virtual void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) override final { + } + virtual void handleBody(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total) override final { + if (_onRequest) { + _contentLength = total; + if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { + request->_tempObject = malloc(total); + } + if (request->_tempObject != NULL) { + memcpy((uint8_t *)(request->_tempObject) + index, data, len); + } + } + } + virtual bool isRequestHandlerTrivial() override final { + return _onRequest ? false : true; + } +}; +#endif diff --git a/lib_standalone/AsyncMqttClient.h b/lib_standalone/AsyncMqttClient.h new file mode 100644 index 000000000..60751f0f9 --- /dev/null +++ b/lib_standalone/AsyncMqttClient.h @@ -0,0 +1,143 @@ +#ifndef ASYNCMQTTCLIENT_H_ +#define ASYNCMQTTCLIENT_H_ + +#include "Arduino.h" +#include + +enum class AsyncMqttClientDisconnectReason : int8_t { + TCP_DISCONNECTED = 0, + + MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1, + MQTT_IDENTIFIER_REJECTED = 2, + MQTT_SERVER_UNAVAILABLE = 3, + MQTT_MALFORMED_CREDENTIALS = 4, + MQTT_NOT_AUTHORIZED = 5, + + ESP8266_NOT_ENOUGH_SPACE = 6, + + TLS_BAD_FINGERPRINT = 7 +}; + +struct AsyncMqttClientMessageProperties { + uint8_t qos; + bool dup; + bool retain; +}; + + +namespace AsyncMqttClientInternals { +// user callbacks +typedef std::function OnConnectUserCallback; +typedef std::function OnDisconnectUserCallback; +typedef std::function OnSubscribeUserCallback; +typedef std::function OnUnsubscribeUserCallback; +typedef std::function OnMessageUserCallback; +typedef std::function OnPublishUserCallback; +}; + +class AsyncMqttClient { + public: + AsyncMqttClient(); + ~AsyncMqttClient(); + + AsyncMqttClient& setKeepAlive(uint16_t keepAlive); + AsyncMqttClient& setClientId(const char* clientId); + AsyncMqttClient& setCleanSession(bool cleanSession); + AsyncMqttClient& setMaxTopicLength(uint16_t maxTopicLength); + AsyncMqttClient& setCredentials(const char* username, const char* password = nullptr); + AsyncMqttClient& setWill(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0) { return *this; } + AsyncMqttClient& setServer(IPAddress ip, uint16_t port); + AsyncMqttClient& setServer(const char* host, uint16_t port); + + AsyncMqttClient& onConnect(AsyncMqttClientInternals::OnConnectUserCallback callback) { return *this; } + AsyncMqttClient& onDisconnect(AsyncMqttClientInternals::OnDisconnectUserCallback callback) { return *this; } + AsyncMqttClient& onSubscribe(AsyncMqttClientInternals::OnSubscribeUserCallback callback) { return *this; } + AsyncMqttClient& onUnsubscribe(AsyncMqttClientInternals::OnUnsubscribeUserCallback callback) { return *this; } + AsyncMqttClient& onMessage(AsyncMqttClientInternals::OnMessageUserCallback callback) { return *this; } + AsyncMqttClient& onPublish(AsyncMqttClientInternals::OnPublishUserCallback callback) { return *this; } + + bool connected() const { return false; } + void connect() {} + void disconnect(bool force = false) {} + uint16_t subscribe(const char* topic, uint8_t qos) {return 0;} + uint16_t unsubscribe(const char* topic) {return 0;} + uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0, bool dup = false, uint16_t message_id = 0) {return 0;} + + const char* getClientId() {return "12";} + + private: +// AsyncClient _client; + + bool _connected; + bool _connectPacketNotEnoughSpace; + bool _disconnectOnPoll; + bool _tlsBadFingerprint; + uint32_t _lastClientActivity; + uint32_t _lastServerActivity; + uint32_t _lastPingRequestTime; + char _generatedClientId[18 + 1]; // esp8266-abc123 and esp32-abcdef123456 + IPAddress _ip; + const char* _host; + bool _useIp; + uint16_t _port; + uint16_t _keepAlive; + bool _cleanSession; + const char* _clientId; + const char* _username; + const char* _password; + const char* _willTopic; + const char* _willPayload; + uint16_t _willPayloadLength; + uint8_t _willQos; + bool _willRetain; + +// std::vector _onConnectUserCallbacks; +// std::vector _onDisconnectUserCallbacks; +// std::vector _onSubscribeUserCallbacks; +// std::vector _onUnsubscribeUserCallbacks; +// std::vector _onMessageUserCallbacks; +// std::vector _onPublishUserCallbacks; + +// AsyncMqttClientInternals::ParsingInformation _parsingInformation; +// AsyncMqttClientInternals::Packet* _currentParsedPacket; +// uint8_t _remainingLengthBufferPosition; +// char _remainingLengthBuffer[4]; + +// uint16_t _nextPacketId; + +// std::vector _pendingPubRels; + +// std::vector _toSendAcks; + +// void _clear(); +// void _freeCurrentParsedPacket(); + + // TCP +// void _onConnect(AsyncClient* client); +// void _onDisconnect(AsyncClient* client); +// static void _onError(AsyncClient* client, int8_t error); +// void _onTimeout(AsyncClient* client, uint32_t time); +// static void _onAck(AsyncClient* client, size_t len, uint32_t time); +// void _onData(AsyncClient* client, char* data, size_t len); +// void _onPoll(AsyncClient* client); + +// // MQTT +// void _onPingResp(); +// void _onConnAck(bool sessionPresent, uint8_t connectReturnCode); +// void _onSubAck(uint16_t packetId, char status); +// void _onUnsubAck(uint16_t packetId); +// void _onMessage(char* topic, char* payload, uint8_t qos, bool dup, bool retain, size_t len, size_t index, size_t total, uint16_t packetId); +// void _onPublish(uint16_t packetId, uint8_t qos); +// void _onPubRel(uint16_t packetId); +// void _onPubAck(uint16_t packetId); +// void _onPubRec(uint16_t packetId); +// void _onPubComp(uint16_t packetId); + +// bool _sendPing(); +// void _sendAcks(); +// bool _sendDisconnect(); + +// uint16_t _getNextPacketId(); +}; + +#endif \ No newline at end of file diff --git a/lib_standalone/AsyncTCP.h b/lib_standalone/AsyncTCP.h new file mode 100644 index 000000000..3b82e964d --- /dev/null +++ b/lib_standalone/AsyncTCP.h @@ -0,0 +1,212 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ASYNCTCP_H_ +#define ASYNCTCP_H_ + +#include "Arduino.h" +#include + +//If core is not defined, then we are running in Arduino or PIO +#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE +#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core +#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event +#endif + +class AsyncClient; + +#define ASYNC_MAX_ACK_TIME 5000 +#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) +#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. + +typedef std::function AcConnectHandler; +typedef std::function AcAckHandler; +typedef std::function AcErrorHandler; +typedef std::function AcDataHandler; +typedef std::function AcPacketHandler; +typedef std::function AcTimeoutHandler; + +struct tcp_pcb; +struct ip_addr; + +class AsyncClient { + public: + AsyncClient(tcp_pcb* pcb = 0); + ~AsyncClient(); + + AsyncClient & operator=(const AsyncClient &other); + AsyncClient & operator+=(const AsyncClient &other); + + bool operator==(const AsyncClient &other); + + bool operator!=(const AsyncClient &other) { + return !(*this == other); + } + bool connect(IPAddress ip, uint16_t port); + bool connect(const char* host, uint16_t port); + void close(bool now = false); + void stop(); + int8_t abort(); + bool free(); + + bool canSend();//ack is not pending + size_t space();//space available in the TCP window + size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending + bool send();//send all data added with the method above + + //write equals add()+send() + size_t write(const char* data); + size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true + + uint8_t state(); + bool connecting(); + bool connected(); + bool disconnecting(); + bool disconnected(); + bool freeable();//disconnected or disconnecting + + uint16_t getMss(); + + uint32_t getRxTimeout(); + void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds + + uint32_t getAckTimeout(); + void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds + + void setNoDelay(bool nodelay); + bool getNoDelay(); + + uint32_t getRemoteAddress(); + uint16_t getRemotePort(); + uint32_t getLocalAddress(); + uint16_t getLocalPort(); + + //compatibility + IPAddress remoteIP(); + uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); + + void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect + void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected + void onAck(AcAckHandler cb, void* arg = 0); //ack received + void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error + void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used) + void onPacket(AcPacketHandler cb, void* arg = 0); //data received + void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout + void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected + + void ackPacket(struct pbuf * pb);//ack pbuf from onPacket + size_t ack(size_t len); //ack data that you have not acked using the method below + void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData + + const char * errorToString(int8_t error); + const char * stateToString(); + + //Do not use any of the functions below! + static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb); + static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err); + static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); + static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); + static void _s_error(void *arg, int8_t err); + static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); + static int8_t _s_connected(void* arg, void* tpcb, int8_t err); + static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg); + + int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err); + tcp_pcb * pcb(){ return _pcb; } + + protected: + tcp_pcb* _pcb; + int8_t _closed_slot; + + AcConnectHandler _connect_cb; + void* _connect_cb_arg; + AcConnectHandler _discard_cb; + void* _discard_cb_arg; + AcAckHandler _sent_cb; + void* _sent_cb_arg; + AcErrorHandler _error_cb; + void* _error_cb_arg; + AcDataHandler _recv_cb; + void* _recv_cb_arg; + AcPacketHandler _pb_cb; + void* _pb_cb_arg; + AcTimeoutHandler _timeout_cb; + void* _timeout_cb_arg; + AcConnectHandler _poll_cb; + void* _poll_cb_arg; + + bool _pcb_busy; + uint32_t _pcb_sent_at; + bool _ack_pcb; + uint32_t _rx_ack_len; + uint32_t _rx_last_packet; + uint32_t _rx_since_timeout; + uint32_t _ack_timeout; + uint16_t _connect_port; + + int8_t _close(); + int8_t _connected(void* pcb, int8_t err); + void _error(int8_t err); + int8_t _poll(tcp_pcb* pcb); + int8_t _sent(tcp_pcb* pcb, uint16_t len); + int8_t _fin(tcp_pcb* pcb, int8_t err); + int8_t _lwip_fin(tcp_pcb* pcb, int8_t err); + void _dns_found(struct ip_addr *ipaddr); + + public: + AsyncClient* prev; + AsyncClient* next; +}; + +class AsyncServer { + public: + AsyncServer(IPAddress addr, uint16_t port) : _port(port), _addr("poep") {}; + + AsyncServer(uint16_t port) : _port(port) {}; + + ~AsyncServer() {}; + void onClient(AcConnectHandler cb, void* arg); + void begin(); + void end(); + void setNoDelay(bool nodelay); + bool getNoDelay(); + uint8_t status(); + + //Do not use any of the functions below! + static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err); + static int8_t _s_accepted(void *arg, AsyncClient* client); + + protected: + uint16_t _port; + IPAddress _addr; + bool _noDelay; + tcp_pcb* _pcb; + AcConnectHandler _connect_cb; + void* _connect_cb_arg; + + int8_t _accept(tcp_pcb* newpcb, int8_t err); + int8_t _accepted(AsyncClient* client); +}; + + +#endif /* ASYNCTCP_H_ */ diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index 71d1a947d..7c40f4ea5 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -2,13 +2,99 @@ #define ESP8266React_h #include +#include +#include +#include +#include +#include +#include +#include + +class DummySettings { + public: + uint8_t tx_mode; + uint8_t ems_bus_id; + bool system_heartbeat; + int8_t syslog_level; // uuid::log::Level + uint32_t syslog_mark_interval; + String syslog_host; + uint8_t master_thermostat; + bool shower_timer; + bool shower_alert; + + uint16_t publish_time; // seconds + uint8_t mqtt_format; // 1=single, 2=nested, 3=ha, 4=custom + uint8_t mqtt_qos; + + String hostname; + String jwtSecret; + String ssid; + String password; + + static void read(DummySettings & settings, JsonObject & root){}; + static void read(DummySettings & settings){}; + + static StateUpdateResult update(JsonObject & root, DummySettings & settings) { + return StateUpdateResult::CHANGED; + } +}; + +class DummySettingsService : public StatefulService { + public: + DummySettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager){}; + + void begin(); + void loop(); + + private: +}; + +#define WiFiSettings DummySettings +#define SecuritySettings DummySettings +#define MqttSettings DummySettings class ESP8266React { - public: - ESP8266React(void* server, void* fs); + public: + ESP8266React(AsyncWebServer * server, FS * fs) + : _settings(server, fs, nullptr){}; - void begin() {}; - void loop() {}; + void begin(){}; + void loop(){}; + + SecurityManager * getSecurityManager() { + return nullptr; + } + + AsyncMqttClient * getMqttClient() { + return _mqttClient; + } + + StatefulService * getWiFiSettingsService() { + return &_settings; + } + + StatefulService * getSecuritySettingsService() { + return &_settings; + } + + StatefulService * getMqttSettingsService() { + return &_settings; + } + + private: + DummySettingsService _settings; + AsyncMqttClient * _mqttClient; +}; + +class EMSESPSettingsService { + public: + EMSESPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager); + + void begin(); + + private: + // HttpEndpoint _httpEndpoint; + // FSPersistence _fsPersistence; }; #endif diff --git a/lib_standalone/ESPAsyncWebServer.h b/lib_standalone/ESPAsyncWebServer.h index e69de29bb..5a0ce867b 100644 --- a/lib_standalone/ESPAsyncWebServer.h +++ b/lib_standalone/ESPAsyncWebServer.h @@ -0,0 +1,521 @@ +#ifndef _ESPAsyncWebServer_H_ +#define _ESPAsyncWebServer_H_ + +#include "Arduino.h" + +#include +#include +#include + +#define DEBUGF(...) //Serial.printf(__VA_ARGS__) + +class AsyncWebServer; +class AsyncWebServerRequest; +class AsyncWebServerResponse; +class AsyncWebHeader; +class AsyncWebParameter; +class AsyncWebRewrite; +class AsyncWebHandler; +class AsyncStaticWebHandler; +class AsyncCallbackWebHandler; +class AsyncResponseStream; +class AsyncJsonResponse; + + + +typedef enum { + HTTP_GET = 0b00000001, + HTTP_POST = 0b00000010, + HTTP_DELETE = 0b00000100, + HTTP_PUT = 0b00001000, + HTTP_PATCH = 0b00010000, + HTTP_HEAD = 0b00100000, + HTTP_OPTIONS = 0b01000000, + HTTP_ANY = 0b01111111, +} WebRequestMethod; + +//if this value is returned when asked for data, packet will not be sent and you will be asked for data again +#define RESPONSE_TRY_AGAIN 0xFFFFFFFF + +typedef uint8_t WebRequestMethodComposite; +typedef std::function ArDisconnectHandler; + +/* + * PARAMETER :: Chainable object to hold GET/POST and FILE parameters + * */ + +class AsyncWebParameter { + private: + String _name; + String _value; + size_t _size; + bool _isForm; + 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; + } +}; + +/* + * HEADER :: Chainable object to hold the headers + * */ + +class AsyncWebHeader { + private: + String _name; + String _value; + + public: + AsyncWebHeader(const String & name, const String & value) + : _name(name) + , _value(value) { + } + AsyncWebHeader(const String & data) + : _name() + , _value() { + } + ~AsyncWebHeader() { + } + const String & name() const { + return _name; + } + const String & value() const { + return _value; + } + String toString() const { + return _value; + } +}; + +/* + * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect + * */ + +typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; + +typedef std::function AwsResponseFiller; +typedef std::function AwsTemplateProcessor; + +class AsyncWebServerRequest { + friend class AsyncWebServer; + friend class AsyncCallbackWebHandler; + + private: + AsyncClient * _client; + AsyncWebServer * _server; + AsyncWebHandler * _handler; + AsyncWebServerResponse * _response; + ArDisconnectHandler _onDisconnectfn; + + String _temp; + uint8_t _parseState; + + uint8_t _version; + WebRequestMethodComposite _method; + String _url; + String _host; + String _contentType; + String _boundary; + String _authorization; + RequestedConnectionType _reqconntype; + void _removeNotInterestingHeaders(); + bool _isDigest; + bool _isMultipart; + bool _isPlainPost; + bool _expectingContinue; + size_t _contentLength; + size_t _parsedLength; + + uint8_t _multiParseState; + uint8_t _boundaryPosition; + size_t _itemStartIndex; + size_t _itemSize; + String _itemName; + String _itemFilename; + String _itemType; + String _itemValue; + uint8_t * _itemBuffer; + size_t _itemBufferIndex; + bool _itemIsFile; + + void _onPoll(); + void _onAck(size_t len, uint32_t time); + void _onError(int8_t error); + void _onTimeout(uint32_t time); + void _onDisconnect(){}; + void _onData(void * buf, size_t len); + + void _addParam(AsyncWebParameter *); + void _addPathParam(const char * param); + + bool _parseReqHead(); + bool _parseReqHeader(); + void _parseLine(); + void _parsePlainPostChar(uint8_t data); + void _parseMultipartPostByte(uint8_t data, bool last); + void _addGetParams(const String & params); + + void _handleUploadStart(); + void _handleUploadByte(uint8_t data, bool last); + void _handleUploadEnd(); + + public: + void * _tempObject; + + 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; + } + const char * methodToString() const; + const char * requestedConnTypeToString() const; + RequestedConnectionType requestedConnType() const { + return _reqconntype; + } + bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); + void onDisconnect(ArDisconnectHandler fn){}; + + //hash is the string representation of: + // base64(user:pass) for basic or + // user:realm:md5(user:realm:pass) for digest + bool authenticate(const char * hash); + 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 addInterestingHeader(const String & name); + + void redirect(const String & url); + + void send(AsyncWebServerResponse * response){}; + void send(AsyncJsonResponse * response){}; + void send(int code, const String & contentType = String(), const String & content = String()){}; + void send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr); + void send(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + void sendChunked(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + void send_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); + void send_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + + + AsyncWebServerResponse * beginResponse(int code, const String & contentType = String(), const String & content = String()); + 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); + AsyncResponseStream * beginResponseStream(const String & contentType, size_t bufferSize = 1460); + AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + + 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 + + AsyncWebHeader * getHeader(const String & name) const; + AsyncWebHeader * getHeader(const __FlashStringHelper * data) const; + AsyncWebHeader * getHeader(size_t num) const; + + size_t params() const; // get arguments count + bool hasParam(const String & name, bool post = false, bool file = false) const; + bool hasParam(const __FlashStringHelper * data, bool post = false, bool file = false) const; + + AsyncWebParameter * getParam(const String & name, bool post = false, bool file = false) const; + AsyncWebParameter * getParam(const __FlashStringHelper * data, bool post, bool file) const; + AsyncWebParameter * getParam(size_t num) const; + + 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 + const String & argName(size_t i) const; // get request argument name by number + bool hasArg(const char * name) const; // check if argument exists + bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists + + const String & pathArg(size_t i) const; + + const String & header(const char * name) const; // get request header value by name + const String & header(const __FlashStringHelper * data) const; // get request header value by F(name) + const String & header(size_t i) const; // get request header value by number + const String & headerName(size_t i) const; // get request header name by number + String urlDecode(const String & text) const; +}; + +/* + * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) + * */ + +typedef std::function ArRequestFilterFunction; + +bool ON_STA_FILTER(AsyncWebServerRequest * request); + +bool ON_AP_FILTER(AsyncWebServerRequest * request); + +/* + * REWRITE :: One instance can be handle any Request (done by the Server) + * */ + +class AsyncWebRewrite { + protected: + String _from; + String _toUrl; + String _params; + ArRequestFilterFunction _filter; + + public: + AsyncWebRewrite(const char * from, const char * to) + : _from(from) + , _toUrl(to) + , _params(String()) + , _filter(NULL) { + } + 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; + } +}; + +/* + * HANDLER :: One instance can be attached to any Request (done by the Server) + * */ + +class AsyncWebHandler { + protected: + 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() { + } + 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; + } +}; + +/* + * 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; + +class AsyncWebServerResponse { + protected: + int _code; + String _contentType; + size_t _contentLength; + bool _sendContentLength; + bool _chunked; + size_t _headLength; + size_t _sentLength; + size_t _ackedLength; + size_t _writtenLength; + WebResponseState _state; + const char * _responseCodeToString(int code); + + public: + AsyncWebServerResponse(); + virtual ~AsyncWebServerResponse(); + virtual void setCode(int code); + virtual void setContentLength(size_t len); + virtual void setContentType(const String & type); + virtual void addHeader(const String & name, const String & value); + virtual String _assembleHead(uint8_t version); + virtual bool _started() const; + virtual bool _finished() const; + virtual bool _failed() const; + virtual bool _sourceValid() const; + virtual void _respond(AsyncWebServerRequest * request); + virtual size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); +}; + +/* + * SERVER :: One instance + * */ + +typedef std::function ArRequestHandlerFunction; +typedef std::function ArUploadHandlerFunction; +typedef std::function ArBodyHandlerFunction; + +class AsyncWebServer { + protected: + AsyncServer _server; + AsyncCallbackWebHandler * _catchAllHandler; + + public: + // proddy + AsyncWebServer(uint16_t port) + : _server(port){}; + // , _rewrites(LinkedList([](AsyncWebRewrite* r){ delete r; })) + // , _handlers(LinkedList([](AsyncWebHandler* h){ delete h; })) + + + ~AsyncWebServer(){}; + + void begin(){}; + void end(); + +#if ASYNC_TCP_SSL_ENABLED + void onSslFileRequest(AcSSlFileHandler cb, void * arg); + void beginSecure(const char * cert, const char * private_key_file, const char * password); +#endif + + AsyncWebRewrite & addRewrite(AsyncWebRewrite * rewrite); + bool removeRewrite(AsyncWebRewrite * rewrite); + AsyncWebRewrite & rewrite(const char * from, const char * to); + + AsyncWebHandler & addHandler(AsyncWebHandler * handler); + bool removeHandler(AsyncWebHandler * handler); + + /* + + AsyncCallbackWebHandler & on(const char * uri, ArRequestHandlerFunction onRequest) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + return *handler; + }; + AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + return handler; + }; + AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + return *handler; + }; + AsyncCallbackWebHandler & + on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + return *handler; + }; + */ + + void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){}; + + void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned + void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads + void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) + + void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody + + void _handleDisconnect(AsyncWebServerRequest * request); + void _attachHandler(AsyncWebServerRequest * request); + void _rewriteRequest(AsyncWebServerRequest * request); +}; + +// class DefaultHeaders { +// headers_t _headers; + +// DefaultHeaders() +// public: + +// void addHeader(const String& name, const String& value){ +// _headers.add(new AsyncWebHeader(name, value)); +// } + +// DefaultHeaders(DefaultHeaders const &) = delete; +// DefaultHeaders &operator=(DefaultHeaders const &) = delete; +// static DefaultHeaders &Instance() { +// static DefaultHeaders instance; +// return instance; +// } +// }; + +// #include "WebResponseImpl.h" +// #include "WebHandlerImpl.h" +// #include "AsyncWebSocket.h" +// #include "AsyncEventSource.h" + +typedef std::function ArJsonRequestHandlerFunction; + +#endif /* _AsyncWebServer_H_ */ diff --git a/lib_standalone/FS.h b/lib_standalone/FS.h new file mode 100644 index 000000000..6024285af --- /dev/null +++ b/lib_standalone/FS.h @@ -0,0 +1,10 @@ +#ifndef FS_h +#define FS_h + +class FS { + bool open() { + return true; + }; +}; + +#endif diff --git a/lib_standalone/FSPersistence.h b/lib_standalone/FSPersistence.h index e69de29bb..754a90cda 100644 --- a/lib_standalone/FSPersistence.h +++ b/lib_standalone/FSPersistence.h @@ -0,0 +1,102 @@ +#ifndef FSPersistence_h +#define FSPersistence_h + +#include +#include + +template +class FSPersistence { + public: + FSPersistence(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService * statefulService, + FS * fs, + char const * filePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) + : _stateReader(stateReader) + , _stateUpdater(stateUpdater) + , _statefulService(statefulService) + , _fs(fs) + , _filePath(filePath) + , _bufferSize(bufferSize) + , _updateHandlerId(0) { + enableUpdateHandler(); + } + + void readFromFS() { + /* + File settingsFile = _fs->open(_filePath, "r"); + + if (settingsFile) { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + DeserializationError error = deserializeJson(jsonDocument, settingsFile); + if (error == DeserializationError::Ok && jsonDocument.is()) { + JsonObject jsonObject = jsonDocument.as(); + _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + settingsFile.close(); + return; + } + settingsFile.close(); + } + */ + // If we reach here we have not been successful in loading the config, + // hard-coded emergency defaults are now applied. + + applyDefaults(); + } + + bool writeToFS() { + // create and populate a new json object + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + JsonObject jsonObject = jsonDocument.to(); + _statefulService->read(jsonObject, _stateReader); + + // serialize it to filesystem + /* + File settingsFile = _fs->open(_filePath, "w"); + + // failed to open file, return false + if (!settingsFile) { + return false; + } + + // serialize the data to the file + serializeJson(jsonDocument, settingsFile); + settingsFile.close(); + */ + return true; + } + + void disableUpdateHandler() { + if (_updateHandlerId) { + _statefulService->removeUpdateHandler(_updateHandlerId); + _updateHandlerId = 0; + } + } + + void enableUpdateHandler() { + if (!_updateHandlerId) { + _updateHandlerId = _statefulService->addUpdateHandler([&](const String & originId) { writeToFS(); }); + } + } + + private: + JsonStateReader _stateReader; + JsonStateUpdater _stateUpdater; + StatefulService * _statefulService; + FS * _fs; + char const * _filePath; + size_t _bufferSize; + update_handler_id_t _updateHandlerId; + + protected: + // We assume the updater supplies sensible defaults if an empty object + // is supplied, this virtual function allows that to be changed. + virtual void applyDefaults() { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + JsonObject jsonObject = jsonDocument.as(); + _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + } +}; + +#endif // end FSPersistence diff --git a/lib_standalone/Features.h b/lib_standalone/Features.h new file mode 100644 index 000000000..d2172972b --- /dev/null +++ b/lib_standalone/Features.h @@ -0,0 +1,37 @@ +#ifndef Features_h +#define Features_h + +#define FT_ENABLED(feature) feature + +// project feature off by default +#ifndef FT_PROJECT +#define FT_PROJECT 0 +#endif + +// security feature on by default +#ifndef FT_SECURITY +#define FT_SECURITY 0 +#endif + +// mqtt feature on by default +#ifndef FT_MQTT +#define FT_MQTT 0 +#endif + +// ntp feature on by default +#ifndef FT_NTP +#define FT_NTP 0 +#endif + +// mqtt feature on by default +#ifndef FT_OTA +#define FT_OTA 0 +#endif + +// upload firmware feature off by default +#ifndef FT_UPLOAD_FIRMWARE +#define FT_UPLOAD_FIRMWARE 0 +#endif + + +#endif diff --git a/lib_standalone/HttpEndpoint.h b/lib_standalone/HttpEndpoint.h index e69de29bb..c5ce9fefe 100644 --- a/lib_standalone/HttpEndpoint.h +++ b/lib_standalone/HttpEndpoint.h @@ -0,0 +1,173 @@ +#ifndef HttpEndpoint_h +#define HttpEndpoint_h + +#include + +#include +#include + +#include +#include + +#define HTTP_ENDPOINT_ORIGIN_ID "http" + +template +class HttpGetEndpoint { + public: + HttpGetEndpoint(JsonStateReader stateReader, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { + /* + server->on(servicePath.c_str(), + HTTP_GET, + securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1), + authenticationPredicate)); + */ + } + + HttpGetEndpoint(JsonStateReader stateReader, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { + /* + server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1)); + */ + } + + protected: + JsonStateReader _stateReader; + StatefulService* _statefulService; + size_t _bufferSize; + + void fetchSettings(AsyncWebServerRequest* request) { + // AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize); + // JsonObject jsonObject = response->getRoot().to(); + // _statefulService->read(jsonObject, _stateReader); + + // response->setLength(); + // request->send(response); + } +}; + +template +class HttpPostEndpoint { + public: + HttpPostEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + /* + _updateHandler( + servicePath, + securityManager->wrapCallback( + std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), + authenticationPredicate), + bufferSize), + */ + _bufferSize(bufferSize) { + //_updateHandler.setMethod(HTTP_POST); + // server->addHandler(&_updateHandler); + } + + HttpPostEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + /* + _updateHandler(servicePath, + std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), + bufferSize), + */ + _bufferSize(bufferSize) { + // _updateHandler.setMethod(HTTP_POST); + // server->addHandler(&_updateHandler); + } + + protected: + JsonStateReader _stateReader; + JsonStateUpdater _stateUpdater; + StatefulService* _statefulService; + //AsyncCallbackJsonWebHandler _updateHandler; + size_t _bufferSize; + + void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) { + if (!json.is()) { + // request->send(400); + return; + } + JsonObject jsonObject = json.as(); + StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + if (outcome == StateUpdateResult::ERROR) { + // request->send(400); + return; + } + if (outcome == StateUpdateResult::CHANGED) { + // request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); }); + } + // AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize); + // jsonObject = response->getRoot().to(); + // _statefulService->read(jsonObject, _stateReader); + // response->setLength(); + // request->send(response); + } +}; + +template +class HttpEndpoint : public HttpGetEndpoint, public HttpPostEndpoint { + public: + HttpEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + HttpGetEndpoint(stateReader, + statefulService, + server, + servicePath, + securityManager, + authenticationPredicate, + bufferSize), + HttpPostEndpoint(stateReader, + stateUpdater, + statefulService, + server, + servicePath, + securityManager, + authenticationPredicate, + bufferSize) { + } + + HttpEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + HttpGetEndpoint(stateReader, statefulService, server, servicePath, bufferSize), + HttpPostEndpoint(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) { + } +}; + +#endif // end HttpEndpoint diff --git a/lib_standalone/SecurityManager.h b/lib_standalone/SecurityManager.h index e69de29bb..1f1258e09 100644 --- a/lib_standalone/SecurityManager.h +++ b/lib_standalone/SecurityManager.h @@ -0,0 +1,102 @@ +#ifndef SecurityManager_h +#define SecurityManager_h + +#include +#include +#include +// #include +#include +#include + +#ifndef FACTORY_JWT_SECRET +#define FACTORY_JWT_SECRET ESPUtils::defaultDeviceValue() +#endif + +#define ACCESS_TOKEN_PARAMATER "access_token" + +#define AUTHORIZATION_HEADER "Authorization" +#define AUTHORIZATION_HEADER_PREFIX "Bearer " +#define AUTHORIZATION_HEADER_PREFIX_LEN 7 + +#define MAX_JWT_SIZE 128 + +class User { + public: + String username; + String password; + bool admin; + + public: + User(String username, String password, bool admin) : username(username), password(password), admin(admin) { + } +}; + +class Authentication { + public: + User* user; + boolean authenticated; + + public: + Authentication(User& user) : user(new User(user)), authenticated(true) { + } + Authentication() : user(nullptr), authenticated(false) { + } + ~Authentication() { + delete (user); + } +}; + +typedef std::function AuthenticationPredicate; + +class AuthenticationPredicates { + public: + static bool NONE_REQUIRED(Authentication& authentication) { + return true; + }; + static bool IS_AUTHENTICATED(Authentication& authentication) { + return authentication.authenticated; + }; + static bool IS_ADMIN(Authentication& authentication) { + return authentication.authenticated && authentication.user->admin; + }; +}; + +class SecurityManager { + public: +#if FT_ENABLED(FT_SECURITY) + /* + * Authenticate, returning the user if found + */ + virtual Authentication authenticate(const String& username, const String& password) = 0; + + /* + * Generate a JWT for the user provided + */ + virtual String generateJWT(User* user) = 0; + +#endif + + /* + * Check the request header for the Authorization token + */ + virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0; + + /** + * Filter a request with the provided predicate, only returning true if the predicate matches. + */ + virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided request to provide validation against an AuthenticationPredicate. + */ + virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided json request callback to provide validation against an AuthenticationPredicate. + */ + virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) = 0; +}; + +#endif // end SecurityManager_h diff --git a/lib_standalone/StatefulService.cpp b/lib_standalone/StatefulService.cpp new file mode 100644 index 000000000..ece6b1fb3 --- /dev/null +++ b/lib_standalone/StatefulService.cpp @@ -0,0 +1,3 @@ +#include + +update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0; diff --git a/lib_standalone/StatefulService.h b/lib_standalone/StatefulService.h new file mode 100644 index 000000000..c6c38e988 --- /dev/null +++ b/lib_standalone/StatefulService.h @@ -0,0 +1,144 @@ +#ifndef StatefulService_h +#define StatefulService_h + +#include +#include + +#include +#include + +#ifndef DEFAULT_BUFFER_SIZE +#define DEFAULT_BUFFER_SIZE 1024 +#endif + +enum class StateUpdateResult { + CHANGED = 0, // The update changed the state and propagation should take place if required + UNCHANGED, // The state was unchanged, propagation should not take place + ERROR // There was a problem updating the state, propagation should not take place +}; + +template +using JsonStateUpdater = std::function; + +template +using JsonStateReader = std::function; + +typedef size_t update_handler_id_t; +typedef std::function StateUpdateCallback; + +typedef struct StateUpdateHandlerInfo { + static update_handler_id_t currentUpdatedHandlerId; + update_handler_id_t _id; + StateUpdateCallback _cb; + bool _allowRemove; + StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : + _id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){}; +} StateUpdateHandlerInfo_t; + +template +class StatefulService { + public: + template +#ifdef ESP32 + StatefulService(Args&&... args) : + _state(std::forward(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) { + } +#else + StatefulService(Args&&... args) : _state(std::forward(args)...) { + } +#endif + + update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) { + if (!cb) { + return 0; + } + StateUpdateHandlerInfo_t updateHandler(cb, allowRemove); + _updateHandlers.push_back(updateHandler); + return updateHandler._id; + } + + void removeUpdateHandler(update_handler_id_t id) { + for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) { + if ((*i)._allowRemove && (*i)._id == id) { + i = _updateHandlers.erase(i); + } else { + ++i; + } + } + } + + StateUpdateResult update(std::function stateUpdater, const String& originId) { + beginTransaction(); + StateUpdateResult result = stateUpdater(_state); + endTransaction(); + if (result == StateUpdateResult::CHANGED) { + callUpdateHandlers(originId); + } + return result; + } + + StateUpdateResult updateWithoutPropagation(std::function stateUpdater) { + beginTransaction(); + StateUpdateResult result = stateUpdater(_state); + endTransaction(); + return result; + } + + StateUpdateResult update(JsonObject& jsonObject, JsonStateUpdater stateUpdater, const String& originId) { + beginTransaction(); + StateUpdateResult result = stateUpdater(jsonObject, _state); + endTransaction(); + if (result == StateUpdateResult::CHANGED) { + callUpdateHandlers(originId); + } + return result; + } + + StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater stateUpdater) { + beginTransaction(); + StateUpdateResult result = stateUpdater(jsonObject, _state); + endTransaction(); + return result; + } + + void read(std::function stateReader) { + beginTransaction(); + stateReader(_state); + endTransaction(); + } + + void read(JsonObject& jsonObject, JsonStateReader stateReader) { + beginTransaction(); + stateReader(_state, jsonObject); + endTransaction(); + } + + void callUpdateHandlers(const String& originId) { + for (const StateUpdateHandlerInfo_t& updateHandler : _updateHandlers) { + updateHandler._cb(originId); + } + } + + protected: + T _state; + + inline void beginTransaction() { +#ifdef ESP32 + xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); +#endif + } + + inline void endTransaction() { +#ifdef ESP32 + xSemaphoreGiveRecursive(_accessMutex); +#endif + } + + private: +#ifdef ESP32 + SemaphoreHandle_t _accessMutex; +#endif + std::list _updateHandlers; +}; + +#endif // end StatefulService_h diff --git a/lib_standalone/WString.cpp b/lib_standalone/WString.cpp new file mode 100644 index 000000000..5fd02b086 --- /dev/null +++ b/lib_standalone/WString.cpp @@ -0,0 +1,863 @@ +/* + WString.cpp - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All rights reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.com + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "WString.h" + +/*********************************************/ +/* Constructors */ +/*********************************************/ + +char * itoa(signed short value, char * result, const unsigned int base) { + // check that the base if valid + if (base < 2 || base > 36) { + *result = '\0'; + return result; + } + + char * ptr = result, *ptr1 = result, tmp_char; + signed short tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)]; + } while (value); + + // Apply negative sign + if (tmp_value < 0) { + *ptr++ = '-'; + } + + *ptr-- = '\0'; + while (ptr1 < ptr) { + tmp_char = *ptr; + *ptr-- = *ptr1; + *ptr1++ = tmp_char; + } + + return result; +} + +char* ltoa(long value, char* result, int base) { + return itoa((int)value, result, base); +} + +char* ultoa(unsigned long value, char* result, int base) { + return itoa((unsigned int)value, result, base); +} + +char * dtostrf(double number, signed char width, unsigned char prec, char *s) { + bool negative = false; + + char* out = s; + + int fillme = width; // how many cells to fill for the integer part + if (prec > 0) { + fillme -= (prec+1); + } + + // Handle negative numbers + if (number < 0.0) { + negative = true; + fillme--; + number = -number; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + // I optimized out most of the divisions + double rounding = 2.0; + for (unsigned int i = 0; i < prec; ++i) + rounding *= 10.0; + rounding = 1.0 / rounding; + + number += rounding; + + // Figure out how big our number really is + double tenpow = 1.0; + int digitcount = 1; + double nextpow; + while (number >= (nextpow = (10.0 * tenpow))) { + tenpow = nextpow; + digitcount++; + } + + // minimal compensation for possible lack of precision (#7087 addition) + // number *= 1 + std::numeric_limits::epsilon(); + + number /= tenpow; + fillme -= digitcount; + + // Pad unused cells with spaces + while (fillme-- > 0) { + *out++ = ' '; + } + + // Handle negative sign + if (negative) *out++ = '-'; + + // Print the digits, and if necessary, the decimal point + digitcount += prec; + int8_t digit = 0; + while (digitcount-- > 0) { + digit = (int8_t)number; + if (digit > 9) digit = 9; // insurance + *out++ = (char)('0' | digit); + if ((digitcount == prec) && (prec > 0)) { + *out++ = '.'; + } + number -= digit; + number *= 10.0; + } + + // make sure the string is terminated + *out = 0; + return s; +} + +String::String(const char *cstr) +{ + init(); + if (cstr) copy(cstr, strlen(cstr)); +} + +String::String(const String &value) +{ + init(); + *this = value; +} + +String::String(const __FlashStringHelper *pstr) +{ + init(); + *this = pstr; +} + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +String::String(String &&rval) +{ + init(); + move(rval); +} +String::String(StringSumHelper &&rval) +{ + init(); + move(rval); +} +#endif + +String::String(char c) +{ + init(); + char buf[2]; + buf[0] = c; + buf[1] = 0; + *this = buf; +} + +String::String(unsigned char value, unsigned char base) +{ + init(); + char buf[1 + 8 * sizeof(unsigned char)]; + itoa(value, buf, base); + *this = buf; +} + +String::String(int value, unsigned char base) +{ + init(); + char buf[2 + 8 * sizeof(int)]; + itoa(value, buf, base); + *this = buf; +} + +String::String(unsigned int value, unsigned char base) +{ + init(); + char buf[1 + 8 * sizeof(unsigned int)]; + itoa(value, buf, base); + *this = buf; +} + +String::String(long value, unsigned char base) +{ + init(); + char buf[2 + 8 * sizeof(long)]; + ltoa(value, buf, base); + *this = buf; +} + +String::String(unsigned long value, unsigned char base) +{ + init(); + char buf[1 + 8 * sizeof(unsigned long)]; + ultoa(value, buf, base); + *this = buf; +} + +String::String(float value, unsigned char decimalPlaces) +{ + init(); + char buf[33]; + *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); +} + +String::String(double value, unsigned char decimalPlaces) +{ + init(); + char buf[33]; + *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); +} + +String::~String() +{ + free(buffer); +} + +/*********************************************/ +/* Memory Management */ +/*********************************************/ + +inline void String::init(void) +{ + buffer = NULL; + capacity = 0; + len = 0; +} + +void String::invalidate(void) +{ + if (buffer) free(buffer); + buffer = NULL; + capacity = len = 0; +} + +unsigned char String::reserve(unsigned int size) +{ + if (buffer && capacity >= size) return 1; + if (changeBuffer(size)) { + if (len == 0) buffer[0] = 0; + return 1; + } + return 0; +} + +unsigned char String::changeBuffer(unsigned int maxStrLen) +{ + char *newbuffer = (char *)realloc(buffer, maxStrLen + 1); + if (newbuffer) { + buffer = newbuffer; + capacity = maxStrLen; + return 1; + } + return 0; +} + +/*********************************************/ +/* Copy and Move */ +/*********************************************/ + +String & String::copy(const char *cstr, unsigned int length) +{ + if (!reserve(length)) { + invalidate(); + return *this; + } + len = length; + strcpy(buffer, cstr); + return *this; +} + +/* +String & String::copy(const char *pstr, unsigned int length) +{ + if (!reserve(length)) { + invalidate(); + return *this; + } + len = length; + strcpy_P(buffer, pstr); + return *this; +} +*/ + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +void String::move(String &rhs) +{ + if (buffer) { + if (rhs && capacity >= rhs.len) { + strcpy(buffer, rhs.buffer); + len = rhs.len; + rhs.len = 0; + return; + } else { + free(buffer); + } + } + buffer = rhs.buffer; + capacity = rhs.capacity; + len = rhs.len; + rhs.buffer = NULL; + rhs.capacity = 0; + rhs.len = 0; +} +#endif + +String & String::operator = (const String &rhs) +{ + if (this == &rhs) return *this; + + if (rhs.buffer) copy(rhs.buffer, rhs.len); + else invalidate(); + + return *this; +} + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +String & String::operator = (String &&rval) +{ + if (this != &rval) move(rval); + return *this; +} + +String & String::operator = (StringSumHelper &&rval) +{ + if (this != &rval) move(rval); + return *this; +} +#endif + +String & String::operator = (const char *cstr) +{ + if (cstr) copy(cstr, strlen(cstr)); + else invalidate(); + + return *this; +} + +String & String::operator = (const __FlashStringHelper *pstr) +{ + // if (pstr) copy(pstr, strlen_P((PGM_P)pstr)); + // else invalidate(); + + return *this; +} + +/*********************************************/ +/* concat */ +/*********************************************/ + +unsigned char String::concat(const String &s) +{ + return concat(s.buffer, s.len); +} + +unsigned char String::concat(const char *cstr, unsigned int length) +{ + unsigned int newlen = len + length; + if (!cstr) return 0; + if (length == 0) return 1; + if (!reserve(newlen)) return 0; + strcpy(buffer + len, cstr); + len = newlen; + return 1; +} + +unsigned char String::concat(const char *cstr) +{ + if (!cstr) return 0; + return concat(cstr, strlen(cstr)); +} + +unsigned char String::concat(char c) +{ + char buf[2]; + buf[0] = c; + buf[1] = 0; + return concat(buf, 1); +} + +unsigned char String::concat(unsigned char num) +{ + char buf[1 + 3 * sizeof(unsigned char)]; + itoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(int num) +{ + char buf[2 + 3 * sizeof(int)]; + itoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(unsigned int num) +{ + char buf[1 + 3 * sizeof(unsigned int)]; + itoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(long num) +{ + char buf[2 + 3 * sizeof(long)]; + ltoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(unsigned long num) +{ + char buf[1 + 3 * sizeof(unsigned long)]; + ultoa(num, buf, 10); + return concat(buf, strlen(buf)); +} + +unsigned char String::concat(float num) +{ + char buf[20]; + char* string = dtostrf(num, 4, 2, buf); + return concat(string, strlen(string)); +} + +unsigned char String::concat(double num) +{ + char buf[20]; + char* string = dtostrf(num, 4, 2, buf); + return concat(string, strlen(string)); +} + +unsigned char String::concat(const __FlashStringHelper * str) +{ + if (!str) return 0; + int length = strlen_P((const char *) str); + if (length == 0) return 1; + unsigned int newlen = len + length; + if (!reserve(newlen)) return 0; + strcpy_P(buffer + len, (const char *) str); + len = newlen; + return 1; +} + +/*********************************************/ +/* Concatenate */ +/*********************************************/ + +StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(rhs.buffer, rhs.len)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr) +{ + StringSumHelper &a = const_cast(lhs); + if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, char c) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(c)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, int num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, long num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, float num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, double num) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(num)) a.invalidate(); + return a; +} + +StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs) +{ + StringSumHelper &a = const_cast(lhs); + if (!a.concat(rhs)) a.invalidate(); + return a; +} + +/*********************************************/ +/* Comparison */ +/*********************************************/ + +int String::compareTo(const String &s) const +{ + if (!buffer || !s.buffer) { + if (s.buffer && s.len > 0) return 0 - *(unsigned char *)s.buffer; + if (buffer && len > 0) return *(unsigned char *)buffer; + return 0; + } + return strcmp(buffer, s.buffer); +} + +unsigned char String::isEmpty() const { + return (len == 0); +} + +unsigned char String::equals(const String &s2) const +{ + return (len == s2.len && compareTo(s2) == 0); +} + +unsigned char String::equals(const char *cstr) const +{ + if (len == 0) return (cstr == NULL || *cstr == 0); + if (cstr == NULL) return buffer[0] == 0; + return strcmp(buffer, cstr) == 0; +} + +unsigned char String::operator<(const String &rhs) const +{ + return compareTo(rhs) < 0; +} + +unsigned char String::operator>(const String &rhs) const +{ + return compareTo(rhs) > 0; +} + +unsigned char String::operator<=(const String &rhs) const +{ + return compareTo(rhs) <= 0; +} + +unsigned char String::operator>=(const String &rhs) const +{ + return compareTo(rhs) >= 0; +} + +unsigned char String::equalsIgnoreCase( const String &s2 ) const +{ + if (this == &s2) return 1; + if (len != s2.len) return 0; + if (len == 0) return 1; + const char *p1 = buffer; + const char *p2 = s2.buffer; + while (*p1) { + if (tolower(*p1++) != tolower(*p2++)) return 0; + } + return 1; +} + +unsigned char String::startsWith( const String &s2 ) const +{ + if (len < s2.len) return 0; + return startsWith(s2, 0); +} + +unsigned char String::startsWith( const String &s2, unsigned int offset ) const +{ + if (offset > len - s2.len || !buffer || !s2.buffer) return 0; + return strncmp( &buffer[offset], s2.buffer, s2.len ) == 0; +} + +unsigned char String::endsWith( const String &s2 ) const +{ + if ( len < s2.len || !buffer || !s2.buffer) return 0; + return strcmp(&buffer[len - s2.len], s2.buffer) == 0; +} + +/*********************************************/ +/* Character Access */ +/*********************************************/ + +char String::charAt(unsigned int loc) const +{ + return operator[](loc); +} + +void String::setCharAt(unsigned int loc, char c) +{ + if (loc < len) buffer[loc] = c; +} + +char & String::operator[](unsigned int index) +{ + static char dummy_writable_char; + if (index >= len || !buffer) { + dummy_writable_char = 0; + return dummy_writable_char; + } + return buffer[index]; +} + +char String::operator[]( unsigned int index ) const +{ + if (index >= len || !buffer) return 0; + return buffer[index]; +} + +void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const +{ + if (!bufsize || !buf) return; + if (index >= len) { + buf[0] = 0; + return; + } + unsigned int n = bufsize - 1; + if (n > len - index) n = len - index; + strncpy((char *)buf, buffer + index, n); + buf[n] = 0; +} + +/*********************************************/ +/* Search */ +/*********************************************/ + +int String::indexOf(char c) const +{ + return indexOf(c, 0); +} + +int String::indexOf( char ch, unsigned int fromIndex ) const +{ + if (fromIndex >= len) return -1; + const char* temp = strchr(buffer + fromIndex, ch); + if (temp == NULL) return -1; + return temp - buffer; +} + +int String::indexOf(const String &s2) const +{ + return indexOf(s2, 0); +} + +int String::indexOf(const String &s2, unsigned int fromIndex) const +{ + if (fromIndex >= len) return -1; + const char *found = strstr(buffer + fromIndex, s2.buffer); + if (found == NULL) return -1; + return found - buffer; +} + +int String::lastIndexOf( char theChar ) const +{ + return lastIndexOf(theChar, len - 1); +} + +int String::lastIndexOf(char ch, unsigned int fromIndex) const +{ + if (fromIndex >= len) return -1; + char tempchar = buffer[fromIndex + 1]; + buffer[fromIndex + 1] = '\0'; + char* temp = strrchr( buffer, ch ); + buffer[fromIndex + 1] = tempchar; + if (temp == NULL) return -1; + return temp - buffer; +} + +int String::lastIndexOf(const String &s2) const +{ + return lastIndexOf(s2, len - s2.len); +} + +int String::lastIndexOf(const String &s2, unsigned int fromIndex) const +{ + if (s2.len == 0 || len == 0 || s2.len > len) return -1; + if (fromIndex >= len) fromIndex = len - 1; + int found = -1; + for (char *p = buffer; p <= buffer + fromIndex; p++) { + p = strstr(p, s2.buffer); + if (!p) break; + if ((unsigned int)(p - buffer) <= fromIndex) found = p - buffer; + } + return found; +} + +String String::substring(unsigned int left, unsigned int right) const +{ + if (left > right) { + unsigned int temp = right; + right = left; + left = temp; + } + String out; + if (left >= len) return out; + if (right > len) right = len; + char temp = buffer[right]; // save the replaced character + buffer[right] = '\0'; + out = buffer + left; // pointer arithmetic + buffer[right] = temp; //restore character + return out; +} + +/*********************************************/ +/* Modification */ +/*********************************************/ + +void String::replace(char find, char replace) +{ + if (!buffer) return; + for (char *p = buffer; *p; p++) { + if (*p == find) *p = replace; + } +} + +void String::replace(const String& find, const String& replace) +{ + if (len == 0 || find.len == 0) return; + int diff = replace.len - find.len; + char *readFrom = buffer; + char *foundAt; + if (diff == 0) { + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { + memcpy(foundAt, replace.buffer, replace.len); + readFrom = foundAt + replace.len; + } + } else if (diff < 0) { + char *writeTo = buffer; + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { + unsigned int n = foundAt - readFrom; + memcpy(writeTo, readFrom, n); + writeTo += n; + memcpy(writeTo, replace.buffer, replace.len); + writeTo += replace.len; + readFrom = foundAt + find.len; + len += diff; + } + strcpy(writeTo, readFrom); + } else { + unsigned int size = len; // compute size needed for result + while ((foundAt = strstr(readFrom, find.buffer)) != NULL) { + readFrom = foundAt + find.len; + size += diff; + } + if (size == len) return; + if (size > capacity && !changeBuffer(size)) return; + int index = len - 1; + while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) { + readFrom = buffer + index + find.len; + memmove(readFrom + diff, readFrom, len - (readFrom - buffer)); + len += diff; + buffer[len] = 0; + memcpy(buffer + index, replace.buffer, replace.len); + index--; + } + } +} + +void String::remove(unsigned int index){ + // Pass the biggest integer as the count. The remove method + // below will take care of truncating it at the end of the + // string. + remove(index, (unsigned int)-1); +} + +void String::remove(unsigned int index, unsigned int count){ + if (index >= len) { return; } + if (count <= 0) { return; } + if (count > len - index) { count = len - index; } + char *writeTo = buffer + index; + len = len - count; + strncpy(writeTo, buffer + index + count,len - index); + buffer[len] = 0; +} + +void String::toLowerCase(void) +{ + if (!buffer) return; + for (char *p = buffer; *p; p++) { + *p = tolower(*p); + } +} + +void String::toUpperCase(void) +{ + if (!buffer) return; + for (char *p = buffer; *p; p++) { + *p = toupper(*p); + } +} + +void String::trim(void) +{ + if (!buffer || len == 0) return; + char *begin = buffer; + while (isspace(*begin)) begin++; + char *end = buffer + len - 1; + while (isspace(*end) && end >= begin) end--; + len = end + 1 - begin; + if (begin > buffer) memcpy(buffer, begin, len); + buffer[len] = 0; +} + +/*********************************************/ +/* Parsing / Conversion */ +/*********************************************/ + +long String::toInt(void) const +{ + if (buffer) return atol(buffer); + return 0; +} + +float String::toFloat(void) const +{ + return float(toDouble()); +} + +double String::toDouble(void) const +{ + if (buffer) return atof(buffer); + return 0; +} \ No newline at end of file diff --git a/lib_standalone/WString.h b/lib_standalone/WString.h new file mode 100644 index 000000000..1a7c9d223 --- /dev/null +++ b/lib_standalone/WString.h @@ -0,0 +1,236 @@ +/* + WString.h - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All right reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.com + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef String_class_h +#define String_class_h + +#define strlen_P strlen +#define strncpy_P strncpy +#define strcmp_P strcmp +#define strcpy_P strcpy + +// #include "pgmspace.h" +// #include "noniso.h" + +#include +#include +#include + +// When compiling programs with this class, the following gcc parameters +// dramatically increase performance and memory (RAM) efficiency, typically +// with little or no increase in code size. +// -felide-constructors +// -std=c++0x + +class __FlashStringHelper; +// #define F(string_literal) (reinterpret_cast(PSTR(string_literal))) + +// An inherited class for holding the result of a concatenation. These +// result objects are assumed to be writable by subsequent concatenations. +class StringSumHelper; + +// The string class +class String +{ + // use a function pointer to allow for "if (s)" without the + // complications of an operator bool(). for more information, see: + // http://www.artima.com/cppsource/safebool.html + typedef void (String::*StringIfHelperType)() const; + void StringIfHelper() const {} + +public: + // constructors + // creates a copy of the initial value. + // if the initial value is null or invalid, or if memory allocation + // fails, the string will be marked as invalid (i.e. "if (s)" will + // be false). + String(const char *cstr = ""); + String(const String &str); + String(const __FlashStringHelper *str); + #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) + String(String &&rval); + String(StringSumHelper &&rval); + #endif + explicit String(char c); + explicit String(unsigned char, unsigned char base=10); + explicit String(int, unsigned char base=10); + explicit String(unsigned int, unsigned char base=10); + explicit String(long, unsigned char base=10); + explicit String(unsigned long, unsigned char base=10); + explicit String(float, unsigned char decimalPlaces=2); + explicit String(double, unsigned char decimalPlaces=2); + ~String(void); + + // memory management + // return true on success, false on failure (in which case, the string + // is left unchanged). reserve(0), if successful, will validate an + // invalid string (i.e., "if (s)" will be true afterwards) + unsigned char reserve(unsigned int size); + inline unsigned int length(void) const {return len;} + + // creates a copy of the assigned value. if the value is null or + // invalid, or if the memory allocation fails, the string will be + // marked as invalid ("if (s)" will be false). + String & operator = (const String &rhs); + String & operator = (const char *cstr); + String & operator = (const __FlashStringHelper *str); + #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) + String & operator = (String &&rval); + String & operator = (StringSumHelper &&rval); + #endif + + // concatenate (works w/ built-in types) + + // returns true on success, false on failure (in which case, the string + // is left unchanged). if the argument is null or invalid, the + // concatenation is considered unsucessful. + unsigned char concat(const String &str); + unsigned char concat(const char *cstr); + unsigned char concat(char c); + unsigned char concat(unsigned char c); + unsigned char concat(int num); + unsigned char concat(unsigned int num); + unsigned char concat(long num); + unsigned char concat(unsigned long num); + unsigned char concat(float num); + unsigned char concat(double num); + unsigned char concat(const __FlashStringHelper * str); + + // if there's not enough memory for the concatenated value, the string + // will be left unchanged (but this isn't signalled in any way) + String & operator += (const String &rhs) {concat(rhs); return (*this);} + String & operator += (const char *cstr) {concat(cstr); return (*this);} + String & operator += (char c) {concat(c); return (*this);} + String & operator += (unsigned char num) {concat(num); return (*this);} + String & operator += (int num) {concat(num); return (*this);} + String & operator += (unsigned int num) {concat(num); return (*this);} + String & operator += (long num) {concat(num); return (*this);} + String & operator += (unsigned long num) {concat(num); return (*this);} + String & operator += (float num) {concat(num); return (*this);} + String & operator += (double num) {concat(num); return (*this);} + String & operator += (const __FlashStringHelper *str){concat(str); return (*this);} + + friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs); + friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr); + friend StringSumHelper & operator + (const StringSumHelper &lhs, char c); + friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, int num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, long num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, float num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, double num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs); + + // comparison (only works w/ Strings and "strings") + operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; } + int compareTo(const String &s) const; + unsigned char equals(const String &s) const; + unsigned char equals(const char *cstr) const; + unsigned char operator == (const String &rhs) const {return equals(rhs);} + unsigned char operator == (const char *cstr) const {return equals(cstr);} + unsigned char operator != (const String &rhs) const {return !equals(rhs);} + unsigned char operator != (const char *cstr) const {return !equals(cstr);} + unsigned char operator < (const String &rhs) const; + unsigned char operator > (const String &rhs) const; + unsigned char operator <= (const String &rhs) const; + unsigned char operator >= (const String &rhs) const; + unsigned char equalsIgnoreCase(const String &s) const; + unsigned char startsWith( const String &prefix) const; + unsigned char startsWith(const String &prefix, unsigned int offset) const; + unsigned char endsWith(const String &suffix) const; + + // character acccess + char charAt(unsigned int index) const; + void setCharAt(unsigned int index, char c); + char operator [] (unsigned int index) const; + char& operator [] (unsigned int index); + void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const + { getBytes((unsigned char *)buf, bufsize, index); } + const char* c_str() const { return buffer; } + char* begin() { return buffer; } + char* end() { return buffer + length(); } + const char* begin() const { return c_str(); } + const char* end() const { return c_str() + length(); } + + // search + int indexOf( char ch ) const; + int indexOf( char ch, unsigned int fromIndex ) const; + int indexOf( const String &str ) const; + int indexOf( const String &str, unsigned int fromIndex ) const; + int lastIndexOf( char ch ) const; + int lastIndexOf( char ch, unsigned int fromIndex ) const; + int lastIndexOf( const String &str ) const; + int lastIndexOf( const String &str, unsigned int fromIndex ) const; + String substring( unsigned int beginIndex ) const { return substring(beginIndex, len); }; + String substring( unsigned int beginIndex, unsigned int endIndex ) const; + + // modification + void replace(char find, char replace); + void replace(const String& find, const String& replace); + void remove(unsigned int index); + void remove(unsigned int index, unsigned int count); + void toLowerCase(void); + void toUpperCase(void); + void trim(void); + + // parsing/conversion + long toInt(void) const; + float toFloat(void) const; + double toDouble(void) const; + + unsigned char isEmpty() const; + +protected: + char *buffer; // the actual char array + unsigned int capacity; // the array length minus one (for the '\0') + unsigned int len; // the String length (not counting the '\0') +protected: + void init(void); + void invalidate(void); + unsigned char changeBuffer(unsigned int maxStrLen); + unsigned char concat(const char *cstr, unsigned int length); + + // copy and move + String & copy(const char *cstr, unsigned int length); + String & copy(const __FlashStringHelper *pstr, unsigned int length); + #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) + void move(String &rhs); + #endif +}; + +class StringSumHelper : public String +{ +public: + StringSumHelper(const String &s) : String(s) {} + StringSumHelper(const char *p) : String(p) {} + StringSumHelper(char c) : String(c) {} + StringSumHelper(unsigned char num) : String(num) {} + StringSumHelper(int num) : String(num) {} + StringSumHelper(unsigned int num) : String(num) {} + StringSumHelper(long num) : String(num) {} + StringSumHelper(unsigned long num) : String(num) {} + StringSumHelper(float num) : String(num) {} + StringSumHelper(double num) : String(num) {} +}; + +#endif // String_class_h \ No newline at end of file diff --git a/lib_standalone/emsuart_standalone.cpp b/lib_standalone/emsuart_standalone.cpp index 4e506d835..990901147 100644 --- a/lib_standalone/emsuart_standalone.cpp +++ b/lib_standalone/emsuart_standalone.cpp @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -#ifdef EMSESP_STANDALONE - #include "emsuart_standalone.h" #pragma GCC diagnostic push @@ -88,5 +86,3 @@ char * EMSuart::hextoa(char * result, const uint8_t value) { } // namespace emsesp #pragma GCC diagnostic pop - -#endif diff --git a/makefile b/makefile index 04e65ad27..9000ae4c9 100644 --- a/makefile +++ b/makefile @@ -26,7 +26,7 @@ CXX_STANDARD := -std=c++11 #---------------------------------------------------------------------- # Defined Symbols #---------------------------------------------------------------------- -DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DEMSESP_DEBUG -DEMSESP_STANDALONE +DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_NO_LED #---------------------------------------------------------------------- # Sources & Files @@ -66,6 +66,7 @@ CPPFLAGS += -Os CFLAGS += $(CPPFLAGS) CFLAGS += -Wall +CFLAGS += -Wno-unused -Wno-restrict CFLAGS += -Wextra CXXFLAGS += $(CFLAGS) -MMD diff --git a/src/console.cpp b/src/console.cpp index 7c5b6ccfe..09ce690df 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -20,7 +20,7 @@ #include "emsesp.h" #include "version.h" -#ifdef EMSESP_DEBUG +#ifdef EMSESP_STANDALONE #include "test/test.h" #endif @@ -285,7 +285,7 @@ void Console::enter_custom_context(Shell & shell, unsigned int context) { // each custom context has the common commands like log, help, exit, su etc void Console::load_standard_commands(unsigned int context) { -#ifdef EMSESP_DEBUG +#ifdef EMSESP_STANDALONE EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(test)}, diff --git a/src/console.h b/src/console.h index 2b27275b9..5b335503c 100644 --- a/src/console.h +++ b/src/console.h @@ -51,10 +51,6 @@ using uuid::log::Level; // clang-format on // common words -#ifdef EMSESP_DEBUG -MAKE_PSTR_WORD(test) -#endif - MAKE_PSTR_WORD(exit) MAKE_PSTR_WORD(help) MAKE_PSTR_WORD(settings) diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index c530aa43a..09bb61763 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -135,10 +135,6 @@ void Solar::publish_values() { doc["energytotal"] = (float)energyTotal_ / 10; } -#ifdef EMSESP_DEBUG - LOG_DEBUG(F("[DEBUG] Performing a solar module publish")); -#endif - Mqtt::publish("sm_data", doc); } diff --git a/src/emsesp.cpp b/src/emsesp.cpp index faba7d9dc..244284f6e 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -30,15 +30,17 @@ AsyncWebServer webServer(80); #if defined(ESP32) ESP8266React EMSESP::esp8266React(&webServer, &SPIFFS); EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &SPIFFS, EMSESP::esp8266React.getSecurityManager()); -#else +#elif defined(ESP8266) ESP8266React EMSESP::esp8266React(&webServer, &LittleFS); EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); +#elif defined(EMSESP_STANDALONE) +FS dummyFS; +ESP8266React EMSESP::esp8266React(&webServer, &dummyFS); +EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager()); #endif -EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); - -EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); - +EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); +EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); EMSESPScanDevicesService EMSESP::emsespScanDevicesService = EMSESPScanDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); std::vector> EMSESP::emsdevices; // array of all the detected EMS devices @@ -622,7 +624,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) { Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data); #ifdef EMSESP_DEBUG // get_uptime is only updated once per loop, does not give the right time - LOG_DEBUG(F("[DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str()); + LOG_TRACE(F("[DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str()); #endif return; // it's an echo } @@ -720,7 +722,7 @@ void EMSESP::start() { #endif esp8266React.begin(); // starts wifi, ap, ota, security, mqtt services - emsespSettingsService.begin(); // load settings + emsespSettingsService.begin(); // load EMS-ESP specific settings from LittleFS console_.start(); // telnet and serial console system_.start(); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings. mqtt_.start(EMSESP::esp8266React.getMqttClient()); // mqtt init @@ -732,17 +734,14 @@ void EMSESP::start() { // loop de loop void EMSESP::loop() { -#ifndef EMSESP_STANDALONE - esp8266React.loop(); -#endif - - system_.loop(); // does LED and checks system health, and syslog service - mqtt_.loop(); // starts mqtt, and sends out anything in the queue - rxservice_.loop(); // process what ever is in the rx queue - txservice_.loop(); // check that the Tx is all ok - shower_.loop(); // check for shower on/off - sensors_.loop(); // this will also send out via MQTT - console_.loop(); // telnet/serial console + esp8266React.loop(); // web + system_.loop(); // does LED and checks system health, and syslog service + mqtt_.loop(); // starts mqtt, and sends out anything in the queue + rxservice_.loop(); // process what ever is in the rx queue + txservice_.loop(); // check that the Tx is all ok + shower_.loop(); // check for shower on/off + sensors_.loop(); // this will also send out via MQTT + console_.loop(); // telnet/serial console // force a query on the EMS devices to fetch latest data at a set interval (1 min) if ((uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) { @@ -753,7 +752,6 @@ void EMSESP::loop() { #if defined(ESP32) delay(1); #endif - } } // namespace emsesp diff --git a/src/emsesp.h b/src/emsesp.h index 5b19bb315..3156888ec 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -64,7 +64,7 @@ class EMSESP { static void publish_all_values(); -#ifdef EMSESP_DEBUG +#ifdef EMSESP_STANDALONE static void run_test(uuid::console::Shell & shell, const std::string & command); // only for testing static void dummy_mqtt_commands(const char * message); static void rx_telegram(const std::vector & data); diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 0b603460d..fbe669579 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -27,9 +27,7 @@ MAKE_PSTR(logger_name, "mqtt") namespace emsesp { -#ifndef EMSESP_STANDALONE AsyncMqttClient * Mqtt::mqttClient_; -#endif // static parameters we make global std::string Mqtt::hostname_; @@ -90,10 +88,6 @@ void Mqtt::subscribe(const uint8_t device_id, const std::string & topic, mqtt_fu if (!exists) { mqtt_functions_.emplace_back(device_id, std::move(full_topic), cb); // register a call back function for a specific telegram type - } else { -#ifdef EMSESP_DEBUG - LOG_DEBUG(F("[DEBUG] Resubscribing to topic %s"), full_topic); -#endif } queue_subscribe_message(topic); // add subscription to queue @@ -120,11 +114,7 @@ void Mqtt::subscribe(const std::string & topic, mqtt_function_p cb) { // sends out top item on publish queue void Mqtt::loop() { // exit if MQTT is not enabled or if there is no WIFI -#ifndef EMSESP_STANDALONE if (!connected()) { -#else - if (false) { -#endif return; } @@ -201,10 +191,7 @@ void Mqtt::on_message(char * topic, char * payload, size_t len) { // convert payload to a null-terminated char string char message[len + 2]; strlcpy(message, payload, len + 1); - -#ifdef EMSESP_DEBUG LOG_DEBUG(F("[DEBUG] Received %s => %s (length %d)"), topic, message, len); -#endif /* // strip out everything until the last / @@ -296,14 +283,13 @@ void Mqtt::start(AsyncMqttClient * mqttClient) { mqtt_qos_ = mqttSettings.mqtt_qos; }); -#ifndef EMSESP_STANDALONE + mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); }); mqttClient_->setWill(make_topic(will_topic_, "status"), 1, true, "offline"); // with qos 1, retain true mqttClient_->onMessage([this](char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { on_message(topic, payload, len); mqttClient_->onPublish([this](uint16_t packetId) { on_publish(packetId); }); }); -#endif } void Mqtt::set_publish_time(uint16_t publish_time) { @@ -320,7 +306,9 @@ void Mqtt::on_connect() { StaticJsonDocument<90> doc; doc["event"] = "start"; doc["version"] = EMSESP_APP_VERSION; - doc["ip"] = WiFi.localIP().toString(); +#ifndef EMSESP_STANDALONE + doc["ip"] = WiFi.localIP().toString(); +#endif publish("info", doc, false); // send with retain off publish("status", "online", true); // say we're alive to the Last Will topic, with retain on @@ -416,11 +404,7 @@ void Mqtt::process_queue() { // if we're subscribing... if (message->operation == Operation::SUBSCRIBE) { LOG_DEBUG(F("Subscribing to topic: %s"), full_topic); -#ifndef EMSESP_STANDALONE uint16_t packet_id = mqttClient_->subscribe(full_topic, mqtt_qos_); -#else - uint16_t packet_id = 1; -#endif if (!packet_id) { LOG_DEBUG(F("Error subscribing to %s, error %d"), full_topic, packet_id); } @@ -437,12 +421,7 @@ void Mqtt::process_queue() { } // else try and publish it - -#ifndef EMSESP_STANDALONE uint16_t packet_id = mqttClient_->publish(full_topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_); -#else - uint16_t packet_id = 1; -#endif LOG_DEBUG(F("Publishing topic %s (#%02d, attempt #%d, pid %d)"), full_topic, mqtt_message.id_, mqtt_message.retry_count_ + 1, packet_id); if (packet_id == 0) { diff --git a/src/mqtt.h b/src/mqtt.h index 56f53132b..96c37683f 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -27,9 +27,7 @@ #include #include -#ifndef EMSESP_STANDALONE #include -#endif #include "helpers.h" #include "system.h" @@ -86,21 +84,13 @@ class Mqtt { static void on_connect(); void disconnect() { -#ifdef EMSESP_STANDALONE - return; -#else mqttClient_->disconnect(); -#endif } void incoming(char * topic, char * payload); // for testing static bool connected() { -#ifdef EMSESP_STANDALONE - return true; -#else return mqttClient_->connected(); -#endif } static uint32_t publish_fails() { @@ -126,9 +116,7 @@ class Mqtt { }; static std::deque mqtt_messages_; -#ifndef EMSESP_STANDALONE static AsyncMqttClient * mqttClient_; -#endif static constexpr size_t MAX_MQTT_MESSAGES = 50; static size_t maximum_mqtt_messages_; diff --git a/src/system.cpp b/src/system.cpp index 9f9678063..3a2209284 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -75,7 +75,6 @@ void System::mqtt_commands(const char * message) { #endif #ifndef EMSESP_STANDALONE - if (doc["D0"] != nullptr) { const int8_t set = doc["D0"]; pinMode(d0_, OUTPUT); @@ -119,7 +118,6 @@ void System::mqtt_commands(const char * message) { } LOG_INFO(F("Port D3 set to %d"), set); } - #endif const char * command = doc["cmd"]; @@ -155,11 +153,10 @@ void System::restart() { // format fs // format the FS. Wipes everything. void System::format(uuid::console::Shell & shell) { - auto msg = F("Resetting all settings to defaults"); + auto msg = F("Formatting file system. This will also reset all settings to their defaults"); shell.logger().warning(msg); shell.flush(); -#ifndef EMSESP_STANDALONE EMSuart::stop(); #if defined(ESP8266) @@ -169,8 +166,6 @@ void System::format(uuid::console::Shell & shell) { #endif System::restart(); - -#endif } // return free heap mem as a percentage @@ -226,10 +221,9 @@ void System::start() { // fetch settings EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { tx_mode_ = settings.tx_mode; }); - EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat_ = settings.system_heartbeat; }); - syslog_init(); + syslog_init(); // init SysLog #if defined(ESP32) LOG_INFO(F("System booted (EMS-ESP version %s ESP32)"), EMSESP_APP_VERSION); diff --git a/src/test/test.h b/src/test/test.h index 251efc228..300ec8796 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -34,6 +34,8 @@ #include "mqtt.h" #include "emsesp.h" +MAKE_PSTR_WORD(test) + namespace emsesp { class Test {