From 0e4edc9571f8fc0df1559525b0780b85279b3984 Mon Sep 17 00:00:00 2001 From: helgeerbe Date: Tue, 2 Aug 2022 18:40:43 +0200 Subject: [PATCH] First version --- .DS_Store | Bin 0 -> 6148 bytes include/Configuration.h | 7 +- include/MqttVedirectPublishing.h | 27 +++ include/WebApi.h | 2 + include/WebApi_vedirect.h | 17 ++ include/defaults.h | 5 +- lib/.DS_Store | Bin 0 -> 6148 bytes lib/Hoymiles/.DS_Store | Bin 0 -> 6148 bytes .../VeDirectFrameHandler.cpp | 210 ++++++++++++++++++ .../VeDirectFrameHandler.h | 63 ++++++ platformio.ini | 144 ++++++------ src/Configuration.cpp | 5 + src/MqttVedirectPublishing.cpp | 74 ++++++ src/WebApi.cpp | 2 + src/WebApi_vedirect.cpp | 103 +++++++++ src/main.cpp | 4 + webapp/src/components/NavBar.vue | 6 + webapp/src/components/VedirectAdminView.vue | 107 +++++++++ webapp/src/components/VedirectInfoView.vue | 77 +++++++ webapp/src/router/index.ts | 12 + 20 files changed, 789 insertions(+), 76 deletions(-) create mode 100644 .DS_Store create mode 100644 include/MqttVedirectPublishing.h create mode 100644 include/WebApi_vedirect.h create mode 100644 lib/.DS_Store create mode 100644 lib/Hoymiles/.DS_Store create mode 100644 lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp create mode 100644 lib/VeDirectFrameHandler/VeDirectFrameHandler.h create mode 100644 src/MqttVedirectPublishing.cpp create mode 100644 src/WebApi_vedirect.cpp create mode 100644 webapp/src/components/VedirectAdminView.vue create mode 100644 webapp/src/components/VedirectInfoView.vue diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f9f60d0a8f94f19a9f55d4bcbfeaf7866207ada5 GIT binary patch literal 6148 zcmeH~Jr2S!425mzfW*>~F$)La1`&c2Z~;_UkSZ}C_8gt(pM}DVD)cPbU+mOs`-Y|# z5#2wpJCR;QR&b+iElf<2FJ+XQ4A;kH9PangN^Xm!72v&0_H&z{0#twsPys4H1!kl` z9^|X_jGl>)LItS6JQT3+LxCG>vIYIqf#4$m*rDu(wa*e@u>x3=Er<$CqZN!+^)bZi z-VT<$t|nVB+C_8t(7dzS6a&*}7cEF&S{)2jfC@|$SVrF4`G16eoBt;*OsN1B_%j8x zJDd)Cyi}g8AFpTib5?EL;GkcQ@b(jc#E#+(+ztE17GO=bASy8a2)GOkRN$uyyZ{;| B5o7=W literal 0 HcmV?d00001 diff --git a/include/Configuration.h b/include/Configuration.h index b453e0667..76cedd722 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -19,7 +19,7 @@ #define MQTT_MAX_PASSWORD_STRLEN 32 #define MQTT_MAX_TOPIC_STRLEN 32 #define MQTT_MAX_LWTVALUE_STRLEN 20 -#define MQTT_MAX_ROOT_CA_CERT_STRLEN 2048 +#define MQTT_MAX_ROOT_CA_CERT_STRLEN 2048 #define INV_MAX_NAME_STRLEN 31 #define INV_MAX_COUNT 10 @@ -72,7 +72,10 @@ struct CONFIG_T { char Mqtt_Hass_Topic[MQTT_MAX_TOPIC_STRLEN + 1]; bool Mqtt_Hass_IndividualPanels; bool Mqtt_Tls; - char Mqtt_RootCaCert[MQTT_MAX_ROOT_CA_CERT_STRLEN + 1]; + char Mqtt_RootCaCert[MQTT_MAX_ROOT_CA_CERT_STRLEN +1]; + + bool Vedirect_Enabled; + bool Vedirect_UpdatesOnly; }; class ConfigurationClass { diff --git a/include/MqttVedirectPublishing.h b/include/MqttVedirectPublishing.h new file mode 100644 index 000000000..e1d3fdfec --- /dev/null +++ b/include/MqttVedirectPublishing.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include "VeDirectFrameHandler.h" +#include "Configuration.h" +#include +#include + +#ifndef VICTRON_PIN_RX +#define VICTRON_PIN_RX 22 +#endif + +#ifndef VICTRON_PIN_TX +#define VICTRON_PIN_TX 21 +#endif + +class MqttVedirectPublishingClass { +public: + void init(); + void loop(); +private: + std::map _kv_map; + VeDirectFrameHandler _myve; + uint32_t _lastPublish; +}; + +extern MqttVedirectPublishingClass MqttVedirectPublishing; \ No newline at end of file diff --git a/include/WebApi.h b/include/WebApi.h index e2b85e7b6..76d6eac24 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -11,6 +11,7 @@ #include "WebApi_sysstatus.h" #include "WebApi_webapp.h" #include "WebApi_ws_live.h" +#include "WebApi_vedirect.h" #include class WebApiClass { @@ -33,6 +34,7 @@ class WebApiClass { WebApiSysstatusClass _webApiSysstatus; WebApiWebappClass _webApiWebapp; WebApiWsLiveClass _webApiWsLive; + WebApiVedirectClass _webApiVedirect; }; extern WebApiClass WebApi; \ No newline at end of file diff --git a/include/WebApi_vedirect.h b/include/WebApi_vedirect.h new file mode 100644 index 000000000..8359dbdc3 --- /dev/null +++ b/include/WebApi_vedirect.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include + +class WebApiVedirectClass { +public: + void init(AsyncWebServer* server); + void loop(); + +private: + void onVedirectStatus(AsyncWebServerRequest* request); + void onVedirectAdminGet(AsyncWebServerRequest* request); + void onVedirectAdminPost(AsyncWebServerRequest* request); + + AsyncWebServer* _server; +}; \ No newline at end of file diff --git a/include/defaults.h b/include/defaults.h index fefd8606e..4068b1931 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -74,4 +74,7 @@ #define MQTT_HASS_ENABLED false #define MQTT_HASS_RETAIN true #define MQTT_HASS_TOPIC "homeassistant/" -#define MQTT_HASS_INDIVIDUALPANELS false \ No newline at end of file +#define MQTT_HASS_INDIVIDUALPANELS false + +#define VEDIRECT_ENABLED false +#define VEDIRECT_UPDATESONLY true \ No newline at end of file diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..322cb9ad2a17abd1b66c5bb239a5c5c123bc1807 GIT binary patch literal 6148 zcmeHK%Sr<=6g{aGszvF-h0FPc{=rzM(uFH4u5CN@0W(eML+EaP$4_u6_%r^6=O$@w znF>BYMDB%~lexJkj}u5H08DG1Hh~6!8eOom$6|{~yLibeA+k?2u8%n~^e{mS^HR13 z{-Of1cZWE|J$G%uU3=fZ8{A-sNje>5eQPsrFm}<#g9Ncy57*Fv#R55d$=EzyZ>-}& zLiCxd^;owUEmK6316-1sGsSWSGxj!QKVy~~qaWdlp~Ovf$jQ!d#+mdSu|pgob;Ey; zl-)MF1DlU!Lcu;gU~ZG7pV>ZnwdD2r>GAyKg!M1*(;xB2ccr;6*Py5hr~<0MHU;EJ zr`75nwNwRE0aak7fSeBzT`&$*Jgz9LxT~1CaupxboiQFsg%}4cJTgMl9|0$W K7OKFnD)0v0d~!4Z literal 0 HcmV?d00001 diff --git a/lib/Hoymiles/.DS_Store b/lib/Hoymiles/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9a874b5768f336915163bb88cd434575b859f936 GIT binary patch literal 6148 zcmeH~Jr2S!425ml0g0s}V-^m;4I%_5-~tF3k&vj^b9A16778<}(6eNJu~Vz<8=6`~ zboab&MFtUB!i}=AFfm2m$tVxGT*u4pe81nUlA49C} z?O@64YO)2RT{MRe%{!}2F))pG(Sih~)xkgosK7*lF7m<7{{#Hn{6A@7N(HFEpDCdI z{ +#include "VeDirectFrameHandler.h" + +char MODULE[] = "VE.Frame"; // Victron seems to use this to find out where logging messages were generated + +// The name of the record that contains the checksum. +static constexpr char checksumTagName[] = "CHECKSUM"; + +VeDirectFrameHandler::VeDirectFrameHandler() : + //mStop(false), // don't know what Victron uses this for, not using + veName(), + veValue(), + frameIndex(0), + veEnd(0), + mState(IDLE), + mChecksum(0), + mTextPointer(0), + tempName(), + tempValue() +{ +} + +/* + * rxData + * This function is called by the application which passes a byte of serial data + * It is unchanged from Victron's example code + */ +void VeDirectFrameHandler::rxData(uint8_t inbyte) +{ + //if (mStop) return; + if ( (inbyte == ':') && (mState != CHECKSUM) ) { + mState = RECORD_HEX; + } + if (mState != RECORD_HEX) { + mChecksum += inbyte; + } + inbyte = toupper(inbyte); + + switch(mState) { + case IDLE: + /* wait for \n of the start of an record */ + switch(inbyte) { + case '\n': + mState = RECORD_BEGIN; + break; + case '\r': /* Skip */ + default: + break; + } + break; + case RECORD_BEGIN: + mTextPointer = mName; + *mTextPointer++ = inbyte; + mState = RECORD_NAME; + break; + case RECORD_NAME: + // The record name is being received, terminated by a \t + switch(inbyte) { + case '\t': + // the Checksum record indicates a EOR + if ( mTextPointer < (mName + sizeof(mName)) ) { + *mTextPointer = 0; /* Zero terminate */ + if (strcmp(mName, checksumTagName) == 0) { + mState = CHECKSUM; + break; + } + } + mTextPointer = mValue; /* Reset value pointer */ + mState = RECORD_VALUE; + break; + default: + // add byte to name, but do no overflow + if ( mTextPointer < (mName + sizeof(mName)) ) + *mTextPointer++ = inbyte; + break; + } + break; + case RECORD_VALUE: + // The record value is being received. The \r indicates a new record. + switch(inbyte) { + case '\n': + // forward record, only if it could be stored completely + if ( mTextPointer < (mValue + sizeof(mValue)) ) { + *mTextPointer = 0; // make zero ended + textRxEvent(mName, mValue); + } + mState = RECORD_BEGIN; + break; + case '\r': /* Skip */ + break; + default: + // add byte to value, but do no overflow + if ( mTextPointer < (mValue + sizeof(mValue)) ) + *mTextPointer++ = inbyte; + break; + } + break; + case CHECKSUM: + { + bool valid = mChecksum == 0; + if (!valid) + logE(MODULE,"[CHECKSUM] Invalid frame"); + mChecksum = 0; + mState = IDLE; + frameEndEvent(valid); + break; + } + case RECORD_HEX: + if (hexRxEvent(inbyte)) { + mChecksum = 0; + mState = IDLE; + } + break; + } +} + +/* + * textRxEvent + * This function is called every time a new name/value is successfully parsed. It writes the values to the temporary buffer. + */ +void VeDirectFrameHandler::textRxEvent(char * mName, char * mValue) { + strcpy(tempName[frameIndex], mName); // copy name to temporary buffer + strcpy(tempValue[frameIndex], mValue); // copy value to temporary buffer + frameIndex++; +} + +/* + * frameEndEvent + * This function is called at the end of the received frame. If the checksum is valid, the temp buffer is read line by line. + * If the name exists in the public buffer, the new value is copied to the public buffer. If not, a new name/value entry + * is created in the public buffer. + */ +void VeDirectFrameHandler::frameEndEvent(bool valid) { + if ( valid ) { + for ( int i = 0; i < frameIndex; i++ ) { // read each name already in the temp buffer + bool nameExists = false; + for ( int j = 0; j <= veEnd; j++ ) { // compare to existing names in the public buffer + if ( strcmp(tempName[i], veName[j]) == 0 ) { + strcpy(veValue[j], tempValue[i]); // overwrite tempValue in the public buffer + nameExists = true; + break; + } + } + if ( !nameExists ) { + strcpy(veName[veEnd], tempName[i]); // write new Name to public buffer + strcpy(veValue[veEnd], tempValue[i]); // write new Value to public buffer + veEnd++; // increment end of public buffer + if ( veEnd >= buffLen ) { // stop any buffer overrun + veEnd = buffLen - 1; + } + } + } + } + frameIndex = 0; // reset frame +} + +/* + * logE + * This function included for continuity and possible future use. + */ +void VeDirectFrameHandler::logE(const char * module, const char * error) { + Serial.print("MODULE: "); + Serial.println(module); + Serial.print("ERROR: "); + Serial.println(error); + return; +} + +/* + * hexRxEvent + * This function included for continuity and possible future use. + */ +bool VeDirectFrameHandler::hexRxEvent(uint8_t inbyte) { + return true; // stubbed out for future +} \ No newline at end of file diff --git a/lib/VeDirectFrameHandler/VeDirectFrameHandler.h b/lib/VeDirectFrameHandler/VeDirectFrameHandler.h new file mode 100644 index 000000000..a12b24813 --- /dev/null +++ b/lib/VeDirectFrameHandler/VeDirectFrameHandler.h @@ -0,0 +1,63 @@ +/* frameHandler.h + * + * Arduino library to read from Victron devices using VE.Direct protocol. + * Derived from Victron framehandler reference implementation. + * + * 2020.05.05 - 0.2 - initial release + * 2021.02.23 - 0.3 - change frameLen to 22 per VE.Direct Protocol version 3.30 + * + */ + +#ifndef FRAMEHANDLER_H_ +#define FRAMEHANDLER_H_ + +#include + +const byte frameLen = 22; // VE.Direct Protocol: max frame size is 18 +const byte nameLen = 9; // VE.Direct Protocol: max name size is 9 including /0 +const byte valueLen = 33; // VE.Direct Protocol: max value size is 33 including /0 +const byte buffLen = 40; // Maximum number of lines possible from the device. Current protocol shows this to be the BMV700 at 33 lines. + + +class VeDirectFrameHandler { + +public: + VeDirectFrameHandler(); + void rxData(uint8_t inbyte); // byte of serial data to be passed by the application + + char veName[buffLen][nameLen] = { }; // public buffer for received names + char veValue[buffLen][valueLen] = { }; // public buffer for received values + + int frameIndex; // which line of the frame are we on + int veEnd; // current size (end) of the public buffer + +private: + //bool mStop; // not sure what Victron uses this for, not using + + enum States { // state machine + IDLE, + RECORD_BEGIN, + RECORD_NAME, + RECORD_VALUE, + CHECKSUM, + RECORD_HEX + }; + + int mState; // current state + + uint8_t mChecksum; // checksum value + + char * mTextPointer; // pointer to the private buffer we're writing to, name or value + + char mName[9]; // buffer for the field name + char mValue[33]; // buffer for the field value + char tempName[frameLen][nameLen]; // private buffer for received names + char tempValue[frameLen][valueLen]; // private buffer for received values + + void textRxEvent(char *, char *); + void frameEndEvent(bool); + void logE(const char *, const char *); + bool hexRxEvent(uint8_t); +}; + +#endif // FRAMEHANDLER_H_ \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index e4bb2c3f0..2c89e9ba2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,73 +1,71 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting -; -; Please visit documentation for the other options and examples -; https://docs.platformio.org/page/projectconf.html - -[platformio] -default_envs = generic - -[env] -framework = arduino -platform = espressif32@>4 - -build_flags = - -D=${PIOENV} - -DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz - -lib_deps = - https://github.com/me-no-dev/ESPAsyncWebServer.git - bblanchon/ArduinoJson @ ^6.19.4 - https://github.com/bertmelis/espMqttClient.git - nrf24/RF24 @ ^1.4.2 - -extra_scripts = - pre:auto_firmware_version.py - -board_build.partitions = partitions_custom.csv -board_build.filesystem = littlefs -monitor_filters = time, colorize, log2file, esp32_exception_decoder -monitor_speed = 115200 -upload_protocol = esptool - - -[env:generic] -board = esp32dev -monitor_port = COM4 -upload_port = COM4 - - -[env:olimex_esp32_poe] -; https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware - -board = esp32-poe -build_flags = ${env.build_flags} - -DHOYMILES_PIN_MISO=15 - -DHOYMILES_PIN_MOSI=2 - -DHOYMILES_PIN_SCLK=14 - -DHOYMILES_PIN_IRQ=13 - -DHOYMILES_PIN_CE=16 - -DHOYMILES_PIN_CS=5 - -DOPENDTU_ETHERNET - -monitor_port = COM3 -upload_port = COM3 - - -[env:d1 mini esp32] -board = wemos_d1_mini32 -build_flags = - ${env.build_flags} - -DHOYMILES_PIN_MISO=19 - -DHOYMILES_PIN_MOSI=23 - -DHOYMILES_PIN_SCLK=18 - -DHOYMILES_PIN_IRQ=16 - -DHOYMILES_PIN_CE=17 - -DHOYMILES_PIN_CS=5 - -monitor_port = /dev/cu.usbserial-01E68DD0 -upload_port = /dev/cu.usbserial-01E68DD0 +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +default_envs = generic + +[env] +framework = arduino +platform = espressif32@>4 + +build_flags = + -D=${PIOENV} + -DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz + +lib_deps = + https://github.com/me-no-dev/ESPAsyncWebServer.git + bblanchon/ArduinoJson @ ^6.19.4 + https://github.com/bertmelis/espMqttClient.git + nrf24/RF24 @ ^1.4.2 + +extra_scripts = + pre:auto_firmware_version.py + +board_build.partitions = partitions_custom.csv +board_build.filesystem = littlefs +monitor_filters = time, colorize, log2file, esp32_exception_decoder +monitor_speed = 115200 +upload_protocol = esptool + +[env:generic] +board = esp32dev +monitor_port = COM4 +upload_port = COM4 + +[env:olimex_esp32_poe] +; https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware + +board = esp32-poe +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=15 + -DHOYMILES_PIN_MOSI=2 + -DHOYMILES_PIN_SCLK=14 + -DHOYMILES_PIN_IRQ=13 + -DHOYMILES_PIN_CE=16 + -DHOYMILES_PIN_CS=5 + -DOPENDTU_ETHERNET + +monitor_port = COM3 +upload_port = COM3 + +[env:d1 mini esp32] +build_flags = + ${env.build_flags} + -DHOYMILES_PIN_MISO=19 + -DHOYMILES_PIN_MOSI=23 + -DHOYMILES_PIN_SCLK=18 + -DHOYMILES_PIN_IRQ=16 + -DHOYMILES_PIN_CE=17 + -DHOYMILES_PIN_CS=5 + -DVICTRON_PIN_TX=21 + -DVICTRON_PIN_RX=22 +board = wemos_d1_mini32 +monitor_port = /dev/cu.usbserial-01E68DD0 +upload_port = /dev/cu.usbserial-01E68DD0 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 095739134..fe403582f 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -56,6 +56,9 @@ void ConfigurationClass::init() config.Mqtt_Hass_Retain = MQTT_HASS_RETAIN; strlcpy(config.Mqtt_Hass_Topic, MQTT_TOPIC, sizeof(config.Mqtt_Hass_Topic)); config.Mqtt_Hass_IndividualPanels = MQTT_HASS_INDIVIDUALPANELS; + + config.Vedirect_Enabled = VEDIRECT_ENABLED; + config.Vedirect_UpdatesOnly = VEDIRECT_UPDATESONLY; } bool ConfigurationClass::write() @@ -145,6 +148,8 @@ void ConfigurationClass::migrate() if (config.Cfg_Version < 0x00011300) { config.Mqtt_Tls = MQTT_TLS; strlcpy(config.Mqtt_RootCaCert, MQTT_ROOT_CA_CERT, sizeof(config.Mqtt_RootCaCert)); + config.Vedirect_Enabled = VEDIRECT_ENABLED; + config.Vedirect_UpdatesOnly = VEDIRECT_UPDATESONLY; } config.Cfg_Version = CONFIG_VERSION; diff --git a/src/MqttVedirectPublishing.cpp b/src/MqttVedirectPublishing.cpp new file mode 100644 index 000000000..a18a90144 --- /dev/null +++ b/src/MqttVedirectPublishing.cpp @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2022 Helge Erbe and others + */ +#include "MqttVedirectPublishing.h" +#include "VeDirectFrameHandler.h" +#include "MqttSettings.h" + + + +MqttVedirectPublishingClass MqttVedirectPublishing; + +void MqttVedirectPublishingClass::init() +{ + Serial2.begin(19200, SERIAL_8N1, VICTRON_PIN_RX, VICTRON_PIN_TX); + Serial2.flush(); +} + +void MqttVedirectPublishingClass::loop() +{ + CONFIG_T& config = Configuration.get(); + + if (!MqttSettings.getConnected() && !config.Vedirect_Enabled) { + return; + } + + if (millis() - _lastPublish > (config.Mqtt_PublishInterval * 1000)) { + String key; + String value; + bool bChanged; + + while ( Serial2.available() ) { + _myve.rxData(Serial2.read()); + } + yield(); + + String topic = ""; + for ( int i = 0; i < _myve.veEnd; i++ ) { + key = _myve.veName[i]; + value = _myve.veValue[i]; + + // just for debug + Serial.print(key.c_str()); + Serial.print("= "); + Serial.println(value.c_str()); + + // Add new key, value pairs to map and update changed values. + // Mark changed values + auto a = _kv_map.find(key); + if (a != _kv_map.end()) { + if (_kv_map[key] == value) { + bChanged = false; + } + else { + _kv_map[key] = value; + bChanged = true; + } + } + else { + _kv_map.insert(std::make_pair(key, value)); + bChanged = true; + } + + // publish only changed key, values pairs + if (!config.Vedirect_UpdatesOnly || (bChanged && config.Vedirect_UpdatesOnly)) { + topic = "victron/"; + topic.concat(key); + topic.replace("#",""); // # is a no go in mqtt topic + MqttSettings.publish(topic.c_str(), value.c_str()); + } + } + _lastPublish = millis(); + } +} \ No newline at end of file diff --git a/src/WebApi.cpp b/src/WebApi.cpp index 1131d3c52..6498634dd 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -29,6 +29,7 @@ void WebApiClass::init() _webApiSysstatus.init(&_server); _webApiWebapp.init(&_server); _webApiWsLive.init(&_server); + _webApiVedirect.init(&_server); _server.begin(); } @@ -45,6 +46,7 @@ void WebApiClass::loop() _webApiSysstatus.loop(); _webApiWebapp.loop(); _webApiWsLive.loop(); + _webApiVedirect.loop(); } WebApiClass WebApi; \ No newline at end of file diff --git a/src/WebApi_vedirect.cpp b/src/WebApi_vedirect.cpp new file mode 100644 index 000000000..ea0e4dcce --- /dev/null +++ b/src/WebApi_vedirect.cpp @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2022 Thomas Basler and others + */ +#include "WebApi_vedirect.h" +#include "ArduinoJson.h" +#include "AsyncJson.h" +#include "Configuration.h" +#include "helper.h" + +void WebApiVedirectClass::init(AsyncWebServer* server) +{ + using namespace std::placeholders; + + _server = server; + + _server->on("/api/vedirect/status", HTTP_GET, std::bind(&WebApiVedirectClass::onVedirectStatus, this, _1)); + _server->on("/api/vedirect/config", HTTP_GET, std::bind(&WebApiVedirectClass::onVedirectAdminGet, this, _1)); + _server->on("/api/vedirect/config", HTTP_POST, std::bind(&WebApiVedirectClass::onVedirectAdminPost, this, _1)); +} + +void WebApiVedirectClass::loop() +{ +} + +void WebApiVedirectClass::onVedirectStatus(AsyncWebServerRequest* request) +{ + AsyncJsonResponse* response = new AsyncJsonResponse(); + JsonObject root = response->getRoot(); + CONFIG_T& config = Configuration.get(); + + root[F("vedirect_enabled")] = config.Vedirect_Enabled; + root[F("vedirect_updatesonly")] = config.Vedirect_UpdatesOnly; + + response->setLength(); + request->send(response); +} + +void WebApiVedirectClass::onVedirectAdminGet(AsyncWebServerRequest* request) +{ + AsyncJsonResponse* response = new AsyncJsonResponse(); + JsonObject root = response->getRoot(); + CONFIG_T& config = Configuration.get(); + + root[F("vedirect_enabled")] = config.Vedirect_Enabled; + root[F("vedirect_updatesonly")] = config.Vedirect_UpdatesOnly; + + response->setLength(); + request->send(response); +} + +void WebApiVedirectClass::onVedirectAdminPost(AsyncWebServerRequest* request) +{ + AsyncJsonResponse* response = new AsyncJsonResponse(); + JsonObject retMsg = response->getRoot(); + retMsg[F("type")] = F("warning"); + + if (!request->hasParam("data", true)) { + retMsg[F("message")] = F("No values found!"); + response->setLength(); + request->send(response); + return; + } + + String json = request->getParam("data", true)->value(); + + if (json.length() > 1024) { + retMsg[F("message")] = F("Data too large!"); + response->setLength(); + request->send(response); + return; + } + + DynamicJsonDocument root(1024); + DeserializationError error = deserializeJson(root, json); + + if (error) { + retMsg[F("message")] = F("Failed to parse data!"); + response->setLength(); + request->send(response); + return; + } + + if (!(root.containsKey("vedirect_enabled"))) { + retMsg[F("message")] = F("Values are missing!"); + response->setLength(); + request->send(response); + return; + } + + CONFIG_T& config = Configuration.get(); + config.Vedirect_Enabled = root[F("vedirect_enabled")].as(); + config.Vedirect_UpdatesOnly = root[F("vedirect_updatesonly")].as(); + Configuration.write(); + + retMsg[F("type")] = F("success"); + retMsg[F("message")] = F("Settings saved!"); + + response->setLength(); + request->send(response); + + +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5c69e2efd..15f7150b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "Hoymiles.h" #include "MqttHassPublishing.h" #include "MqttPublishing.h" +#include "MqttVedirectPublishing.h" #include "MqttSettings.h" #include "NtpSettings.h" #include "WebApi.h" @@ -68,6 +69,7 @@ void setup() Serial.print(F("Initialize MqTT... ")); MqttSettings.init(); MqttPublishing.init(); + MqttVedirectPublishing.init(); MqttHassPublishing.init(); Serial.println(F("done")); @@ -106,6 +108,8 @@ void loop() yield(); MqttPublishing.loop(); yield(); + MqttVedirectPublishing.loop(); + yield(); MqttHassPublishing.loop(); yield(); WebApi.loop(); diff --git a/webapp/src/components/NavBar.vue b/webapp/src/components/NavBar.vue index ec508f88d..ea945c9c1 100644 --- a/webapp/src/components/NavBar.vue +++ b/webapp/src/components/NavBar.vue @@ -32,6 +32,9 @@
  • DTU Settings +
  • +
  • + Ve.direct Settings
  • @@ -59,6 +62,9 @@
  • MqTT
  • +
  • + Ve.direct +