diff --git a/README.md b/README.md index 36f764a2..ec52d9e9 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with th ## Screenshots Several screenshots of the frontend can be found here: [Screenshots](docs/screenshots/README.md) +## Builds +Different builds from existing installations can be found here [Builds](docs/builds/README.md) +Like to show your own build? Just send me a Pull Request. + I extended the original OpenDTU software to support also Victron's Ve.Direct protocol on the same chip. Additional information about Ve.direct can be downloaded from https://www.victronenergy.com/support-and-downloads/technical-information. Web-Live-Interface: diff --git a/docs/README.md b/docs/README.md index eac94f93..bec34576 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,3 +4,4 @@ More detailed descriptions for some topics can be found here. ## [MQTT Topic Documentation](MQTT_Topics.md) ## [Web API Documentation](Web-API.md) +## [Builds](builds/README.md) \ No newline at end of file diff --git a/docs/builds/202654506-8a4ac4ef-c883-490e-8ee1-1e1f7fa34972.jpg b/docs/builds/202654506-8a4ac4ef-c883-490e-8ee1-1e1f7fa34972.jpg new file mode 100644 index 00000000..b3d7843d Binary files /dev/null and b/docs/builds/202654506-8a4ac4ef-c883-490e-8ee1-1e1f7fa34972.jpg differ diff --git a/docs/builds/README.md b/docs/builds/README.md new file mode 100644 index 00000000..38b7b7ab --- /dev/null +++ b/docs/builds/README.md @@ -0,0 +1,19 @@ +# Builds using different boards + +## ESP32 Dev Board +### Build by @Marc-- +* Used build environment: generic +* Case: https://www.thingiverse.com/thing:5435911 +![](large_display_PXL_20220715_145622277.jpg) + +### Build by @cepresso +* Used build environment: generic +* Case: https://www.printables.com/de/model/293003-sol-opendtu-esp32-nrf24l01-case +![](sol.webp) + +## LILYGO® TTGO T-Internet-POE +### Build by @fromCologne +* Used build environment: LilyGO_T_ETH_POE +* Board info: http://www.lilygo.cn/claprod_view.aspx?TypeId=21&Id=1344&FId=t28:21:28 +* Case: https://www.thingiverse.com/thing:5244895 +![](202654506-8a4ac4ef-c883-490e-8ee1-1e1f7fa34972.jpg) \ No newline at end of file diff --git a/docs/builds/large_display_PXL_20220715_145622277.jpg b/docs/builds/large_display_PXL_20220715_145622277.jpg new file mode 100644 index 00000000..23c57156 Binary files /dev/null and b/docs/builds/large_display_PXL_20220715_145622277.jpg differ diff --git a/docs/builds/sol.webp b/docs/builds/sol.webp new file mode 100644 index 00000000..0faef31a Binary files /dev/null and b/docs/builds/sol.webp differ diff --git a/include/Configuration.h b/include/Configuration.h index 57c3233a..4628e8a3 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -3,9 +3,8 @@ #include -#define CONFIG_FILENAME "/config.bin" -#define CONFIG_FILENAME_JSON "/config.json" -#define CONFIG_VERSION 0x00011600 // 0.1.22 // make sure to clean all after change +#define CONFIG_FILENAME "/config.json" +#define CONFIG_VERSION 0x00011700 // 0.1.23 // make sure to clean all after change #define WIFI_MAX_SSID_STRLEN 31 #define WIFI_MAX_PASSWORD_STRLEN 64 @@ -15,10 +14,9 @@ #define NTP_MAX_TIMEZONE_STRLEN 50 #define NTP_MAX_TIMEZONEDESCR_STRLEN 50 -#define MQTT_MAX_HOSTNAME_OLD_STRLEN 31 #define MQTT_MAX_HOSTNAME_STRLEN 128 -#define MQTT_MAX_USERNAME_STRLEN 32 -#define MQTT_MAX_PASSWORD_STRLEN 32 +#define MQTT_MAX_USERNAME_STRLEN 64 +#define MQTT_MAX_PASSWORD_STRLEN 64 #define MQTT_MAX_TOPIC_STRLEN 32 #define MQTT_MAX_LWTVALUE_STRLEN 20 #define MQTT_MAX_ROOT_CA_CERT_STRLEN 2048 @@ -27,12 +25,19 @@ #define INV_MAX_COUNT 10 #define INV_MAX_CHAN_COUNT 4 +#define CHAN_MAX_NAME_STRLEN 31 + #define JSON_BUFFER_SIZE 6144 +struct CHANNEL_CONFIG_T { + uint16_t MaxChannelPower; + char Name[CHAN_MAX_NAME_STRLEN]; +}; + struct INVERTER_CONFIG_T { uint64_t Serial; char Name[INV_MAX_NAME_STRLEN + 1]; - uint16_t MaxChannelPower[INV_MAX_CHAN_COUNT]; + CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT]; }; struct CONFIG_T { @@ -54,7 +59,6 @@ struct CONFIG_T { char Ntp_TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1]; bool Mqtt_Enabled; - char Mqtt_Hostname_Short[MQTT_MAX_HOSTNAME_OLD_STRLEN + 1]; // Deprecated but for config compatibility uint Mqtt_Port; char Mqtt_Username[MQTT_MAX_USERNAME_STRLEN + 1]; char Mqtt_Password[MQTT_MAX_PASSWORD_STRLEN + 1]; @@ -98,9 +102,7 @@ public: CONFIG_T& get(); INVERTER_CONFIG_T* getFreeInverterSlot(); - -private: - bool readJson(); + INVERTER_CONFIG_T* getInverterConfig(uint64_t serial); }; extern ConfigurationClass Configuration; \ No newline at end of file diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index aabb455f..a9d03135 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -15,6 +15,7 @@ const devInfo_t devInfo[] = { { { 0x10, 0x10, 0x40, ALL }, 400, "HM-400" }, { { 0x10, 0x11, 0x10, ALL }, 600, "HM-600" }, { { 0x10, 0x11, 0x20, ALL }, 700, "HM-700" }, + { { 0x10, 0x11, 0x30, ALL }, 800, "HM-800" }, { { 0x10, 0x11, 0x40, ALL }, 800, "HM-800" }, { { 0x10, 0x12, 0x10, ALL }, 1200, "HM-1200" }, { { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500" }, diff --git a/platformio.ini b/platformio.ini index 7295d4ab..ca07c8b4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -36,8 +36,8 @@ monitor_speed = 115200 upload_protocol = esptool ; Specify port here. Comment out (add ; in front of line) to use auto detection. -monitor_port = COM4 -upload_port = COM4 +monitor_port = COM5 +upload_port = COM5 [env:generic] @@ -95,4 +95,22 @@ build_flags = ${env.build_flags} -DHOYMILES_PIN_IRQ=33 -DHOYMILES_PIN_CE=14 -DHOYMILES_PIN_CS=15 - -DOPENDTU_ETHERNET \ No newline at end of file + -DOPENDTU_ETHERNET + +[env:LilyGO_T_ETH_POE] +; http://www.lilygo.cn/claprod_view.aspx?TypeId=21&Id=1344&FId=t28:21:28 +board = esp32dev +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=2 + -DHOYMILES_PIN_MOSI=15 + -DHOYMILES_PIN_SCLK=14 + -DHOYMILES_PIN_IRQ=34 + -DHOYMILES_PIN_CE=12 + -DHOYMILES_PIN_CS=4 + -DOPENDTU_ETHERNET + -DETH_CLK_MODE=ETH_CLOCK_GPIO17_OUT + -DETH_POWER_PIN=-1 + -DETH_TYPE=ETH_PHY_LAN8720 + -DETH_ADDR=0 + -DETH_MDC_PIN=23 + -DETH_MDIO_PIN=18 \ No newline at end of file diff --git a/src/Configuration.cpp b/src/Configuration.cpp index e6e5d40c..c66553be 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -12,63 +12,11 @@ CONFIG_T config; void ConfigurationClass::init() { memset(&config, 0x0, sizeof(config)); - config.Cfg_SaveCount = 0; - config.Cfg_Version = CONFIG_VERSION; - - // WiFi Settings - strlcpy(config.WiFi_Ssid, WIFI_SSID, sizeof(config.WiFi_Ssid)); - strlcpy(config.WiFi_Password, WIFI_PASSWORD, sizeof(config.WiFi_Password)); - config.WiFi_Dhcp = WIFI_DHCP; - strlcpy(config.WiFi_Hostname, APP_HOSTNAME, sizeof(config.WiFi_Hostname)); - - // NTP Settings - strlcpy(config.Ntp_Server, NTP_SERVER, sizeof(config.Ntp_Server)); - strlcpy(config.Ntp_Timezone, NTP_TIMEZONE, sizeof(config.Ntp_Timezone)); - strlcpy(config.Ntp_TimezoneDescr, NTP_TIMEZONEDESCR, sizeof(config.Ntp_TimezoneDescr)); - - // MqTT Settings - config.Mqtt_Enabled = MQTT_ENABLED; - strlcpy(config.Mqtt_Hostname, MQTT_HOST, sizeof(config.Mqtt_Hostname)); - config.Mqtt_Port = MQTT_PORT; - strlcpy(config.Mqtt_Username, MQTT_USER, sizeof(config.Mqtt_Username)); - strlcpy(config.Mqtt_Password, MQTT_PASSWORD, sizeof(config.Mqtt_Password)); - strlcpy(config.Mqtt_Topic, MQTT_TOPIC, sizeof(config.Mqtt_Topic)); - config.Mqtt_Retain = MQTT_RETAIN; - config.Mqtt_Tls = MQTT_TLS; - strlcpy(config.Mqtt_RootCaCert, MQTT_ROOT_CA_CERT, sizeof(config.Mqtt_RootCaCert)); - strlcpy(config.Mqtt_LwtTopic, MQTT_LWT_TOPIC, sizeof(config.Mqtt_LwtTopic)); - strlcpy(config.Mqtt_LwtValue_Online, MQTT_LWT_ONLINE, sizeof(config.Mqtt_LwtValue_Online)); - strlcpy(config.Mqtt_LwtValue_Offline, MQTT_LWT_OFFLINE, sizeof(config.Mqtt_LwtValue_Offline)); - config.Mqtt_PublishInterval = MQTT_PUBLISH_INTERVAL; - - for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { - config.Inverter[i].Serial = 0; - strlcpy(config.Inverter[i].Name, "", 0); - for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { - config.Inverter[0].MaxChannelPower[c] = 0; - } - } - - config.Dtu_Serial = DTU_SERIAL; - config.Dtu_PollInterval = DTU_POLL_INTERVAL; - config.Dtu_PaLevel = DTU_PA_LEVEL; - - config.Mqtt_Hass_Enabled = MQTT_HASS_ENABLED; - config.Mqtt_Hass_Expire = MQTT_HASS_EXPIRE; - config.Mqtt_Hass_Retain = MQTT_HASS_RETAIN; - strlcpy(config.Mqtt_Hass_Topic, MQTT_HASS_TOPIC, sizeof(config.Mqtt_Hass_Topic)); - config.Mqtt_Hass_IndividualPanels = MQTT_HASS_INDIVIDUALPANELS; - - strlcpy(config.Security_Password, ACCESS_POINT_PASSWORD, sizeof(config.Security_Password)); - - config.Vedirect_Enabled = VEDIRECT_ENABLED; - config.Vedirect_UpdatesOnly = VEDIRECT_UPDATESONLY; - config.Vedirect_PollInterval = VEDIRECT_POLL_INTERVAL; } bool ConfigurationClass::write() { - File f = LittleFS.open(CONFIG_FILENAME_JSON, "w"); + File f = LittleFS.open(CONFIG_FILENAME, "w"); if (!f) { return false; } @@ -136,9 +84,11 @@ bool ConfigurationClass::write() inv["serial"] = config.Inverter[i].Serial; inv["name"] = config.Inverter[i].Name; - JsonArray channels = inv.createNestedArray("channels"); + JsonArray channel = inv.createNestedArray("channel"); for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { - channels.add(config.Inverter[i].MaxChannelPower[c]); + JsonObject chanData = channel.createNestedObject(); + chanData["name"] = config.Inverter[i].channel[c].Name; + chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower; } } @@ -159,30 +109,7 @@ bool ConfigurationClass::write() bool ConfigurationClass::read() { - if (!LittleFS.exists(CONFIG_FILENAME_JSON)) { - Serial.println("Converting binary config to json... "); - File f = LittleFS.open(CONFIG_FILENAME, "r"); - if (!f) { - return false; - } - uint8_t* bytes = reinterpret_cast(&config); - for (unsigned int i = 0; i < sizeof(CONFIG_T); i++) { - bytes[i] = f.read(); - } - f.close(); - write(); - Serial.println("done"); - LittleFS.remove(CONFIG_FILENAME); - } - return readJson(); -} - -bool ConfigurationClass::readJson() -{ - File f = LittleFS.open(CONFIG_FILENAME_JSON, "r", false); - if (!f) { - return false; - } + File f = LittleFS.open(CONFIG_FILENAME, "r", false); DynamicJsonDocument doc(JSON_BUFFER_SIZE); // Deserialize the JSON document @@ -282,9 +209,10 @@ bool ConfigurationClass::readJson() config.Inverter[i].Serial = inv["serial"] | 0ULL; strlcpy(config.Inverter[i].Name, inv["name"] | "", sizeof(config.Inverter[i].Name)); - JsonArray channels = inv["channels"]; + JsonArray channel = inv["channel"]; for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { - config.Inverter[i].MaxChannelPower[c] = channels[c]; + config.Inverter[i].channel[c].MaxChannelPower = channel[c]["max_power"] | 0; + strlcpy(config.Inverter[i].channel[c].Name, channel[c]["name"] | "", sizeof(config.Inverter[i].channel[c].Name)); } } @@ -299,81 +227,35 @@ bool ConfigurationClass::readJson() void ConfigurationClass::migrate() { - if (config.Cfg_Version < 0x00010400) { - strlcpy(config.Ntp_Server, NTP_SERVER, sizeof(config.Ntp_Server)); - strlcpy(config.Ntp_Timezone, NTP_TIMEZONE, sizeof(config.Ntp_Timezone)); - strlcpy(config.Ntp_TimezoneDescr, NTP_TIMEZONEDESCR, sizeof(config.Ntp_TimezoneDescr)); - } - - if (config.Cfg_Version < 0x00010500) { - config.Mqtt_Enabled = MQTT_ENABLED; - strlcpy(config.Mqtt_Hostname, MQTT_HOST, sizeof(config.Mqtt_Hostname)); - config.Mqtt_Port = MQTT_PORT; - strlcpy(config.Mqtt_Username, MQTT_USER, sizeof(config.Mqtt_Username)); - strlcpy(config.Mqtt_Password, MQTT_PASSWORD, sizeof(config.Mqtt_Password)); - strlcpy(config.Mqtt_Topic, MQTT_TOPIC, sizeof(config.Mqtt_Topic)); - } - - if (config.Cfg_Version < 0x00010600) { - config.Mqtt_Retain = MQTT_RETAIN; - } - - if (config.Cfg_Version < 0x00010700) { - strlcpy(config.Mqtt_LwtTopic, MQTT_LWT_TOPIC, sizeof(config.Mqtt_LwtTopic)); - strlcpy(config.Mqtt_LwtValue_Online, MQTT_LWT_ONLINE, sizeof(config.Mqtt_LwtValue_Online)); - strlcpy(config.Mqtt_LwtValue_Offline, MQTT_LWT_OFFLINE, sizeof(config.Mqtt_LwtValue_Offline)); - } - - if (config.Cfg_Version < 0x00010800) { - for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { - config.Inverter[i].Serial = 0; - strlcpy(config.Inverter[i].Name, "", 0); + if (config.Cfg_Version < 0x00011700) { + File f = LittleFS.open(CONFIG_FILENAME, "r", false); + if (!f) { + Serial.println(F("Failed to open file, cancel migration")); + return; } - } - if (config.Cfg_Version < 0x00010900) { - config.Dtu_Serial = DTU_SERIAL; - config.Dtu_PollInterval = DTU_POLL_INTERVAL; - config.Dtu_PaLevel = DTU_PA_LEVEL; - } + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + // Deserialize the JSON document + DeserializationError error = deserializeJson(doc, f); + if (error) { + Serial.println(F("Failed to read file, cancel migration")); + return; + } - if (config.Cfg_Version < 0x00011000) { - config.Mqtt_PublishInterval = MQTT_PUBLISH_INTERVAL; - } - - if (config.Cfg_Version < 0x00011100) { - init(); // Config will be completly incompatible after this update - } - - if (config.Cfg_Version < 0x00011200) { - config.Mqtt_Hass_Enabled = MQTT_HASS_ENABLED; - config.Mqtt_Hass_Retain = MQTT_HASS_RETAIN; - strlcpy(config.Mqtt_Hass_Topic, MQTT_HASS_TOPIC, sizeof(config.Mqtt_Hass_Topic)); - config.Mqtt_Hass_IndividualPanels = MQTT_HASS_INDIVIDUALPANELS; - } - - 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.Vedirect_PollInterval = VEDIRECT_POLL_INTERVAL; - } - - if (config.Cfg_Version < 0x00011400) { - strlcpy(config.Mqtt_Hostname, config.Mqtt_Hostname_Short, sizeof(config.Mqtt_Hostname_Short)); - } - - if (config.Cfg_Version < 0x00011500) { - config.Mqtt_Hass_Expire = MQTT_HASS_EXPIRE; - } - - if (config.Cfg_Version < 0x00011600) { - strlcpy(config.Security_Password, ACCESS_POINT_PASSWORD, sizeof(config.Security_Password)); + JsonArray inverters = doc["inverters"]; + for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { + JsonObject inv = inverters[i].as(); + JsonArray channels = inv["channels"]; + for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { + config.Inverter[i].channel[c].MaxChannelPower = channels[c]; + strlcpy(config.Inverter[i].channel[c].Name, "", sizeof(config.Inverter[i].channel[c].Name)); + } + } } config.Cfg_Version = CONFIG_VERSION; write(); + read(); } CONFIG_T& ConfigurationClass::get() @@ -392,4 +274,15 @@ INVERTER_CONFIG_T* ConfigurationClass::getFreeInverterSlot() return NULL; } +INVERTER_CONFIG_T* ConfigurationClass::getInverterConfig(uint64_t serial) +{ + for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { + if (config.Inverter[i].Serial == serial) { + return &config.Inverter[i]; + } + } + + return NULL; +} + ConfigurationClass Configuration; \ No newline at end of file diff --git a/src/MqttPublishing.cpp b/src/MqttPublishing.cpp index f297d21e..9bc73b05 100644 --- a/src/MqttPublishing.cpp +++ b/src/MqttPublishing.cpp @@ -83,6 +83,12 @@ void MqttPublishingClass::loop() // Loop all channels for (uint8_t c = 0; c <= inv->Statistics()->getChannelCount(); c++) { + if (c > 0) { + INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial()); + if (inv_cfg != nullptr) { + MqttSettings.publish(inv->serialString() + "/" + String(c) + "/name", inv_cfg->channel[c - 1].Name); + } + } for (uint8_t f = 0; f < sizeof(_publishFields); f++) { publishField(inv, c, _publishFields[f]); } diff --git a/src/WebApi_config.cpp b/src/WebApi_config.cpp index fe522652..84e6af95 100644 --- a/src/WebApi_config.cpp +++ b/src/WebApi_config.cpp @@ -37,7 +37,7 @@ void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request) return; } - request->send(LittleFS, CONFIG_FILENAME_JSON, String(), true); + request->send(LittleFS, CONFIG_FILENAME, String(), true); } void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) @@ -96,7 +96,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) response->setLength(); request->send(response); - LittleFS.remove(CONFIG_FILENAME_JSON); + LittleFS.remove(CONFIG_FILENAME); ESP.restart(); } @@ -127,7 +127,7 @@ void WebApiConfigClass::onConfigUpload(AsyncWebServerRequest* request, String fi if (!index) { // open the file on first call and store the file handle in the request object - request->_tempFile = LittleFS.open(CONFIG_FILENAME_JSON, "w"); + request->_tempFile = LittleFS.open(CONFIG_FILENAME, "w"); } if (len) { diff --git a/src/WebApi_inverter.cpp b/src/WebApi_inverter.cpp index 2fb254f9..a99fe7c2 100644 --- a/src/WebApi_inverter.cpp +++ b/src/WebApi_inverter.cpp @@ -62,8 +62,11 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request) max_channels = inv->Statistics()->getChannelCount(); } + JsonArray channel = obj.createNestedArray("channel"); for (uint8_t c = 0; c < max_channels; c++) { - obj[F("max_power")][c] = config.Inverter[i].MaxChannelPower[c]; + JsonObject chanData = channel.createNestedObject(); + chanData["name"] = config.Inverter[i].channel[c].Name; + chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower; } } } @@ -154,7 +157,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request) if (inv != nullptr) { for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { - inv->Statistics()->setChannelMaxPower(c, inverter->MaxChannelPower[c]); + inv->Statistics()->setChannelMaxPower(c, inverter->channel[c].MaxChannelPower); } } @@ -197,7 +200,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) return; } - if (!(root.containsKey("id") && root.containsKey("serial") && root.containsKey("name") && root.containsKey("max_power"))) { + if (!(root.containsKey("id") && root.containsKey("serial") && root.containsKey("name") && root.containsKey("channel"))) { retMsg[F("message")] = F("Values are missing!"); response->setLength(); request->send(response); @@ -225,8 +228,8 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) return; } - JsonArray maxPowerArray = root[F("max_power")].as(); - if (maxPowerArray.size() == 0 || maxPowerArray.size() > INV_MAX_CHAN_COUNT) { + JsonArray channelArray = root[F("channel")].as(); + if (channelArray.size() == 0 || channelArray.size() > INV_MAX_CHAN_COUNT) { retMsg[F("message")] = F("Invalid amount of max channel setting given!"); response->setLength(); request->send(response); @@ -243,8 +246,9 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) strncpy(inverter.Name, root[F("name")].as().c_str(), INV_MAX_NAME_STRLEN); uint8_t arrayCount = 0; - for (JsonVariant maxPower : maxPowerArray) { - inverter.MaxChannelPower[arrayCount] = maxPower.as(); + for (JsonVariant channel : channelArray) { + inverter.channel[arrayCount].MaxChannelPower = channel[F("max_power")].as(); + strncpy(inverter.channel[arrayCount].Name, channel[F("name")] | "", sizeof(inverter.channel[arrayCount].Name)); arrayCount++; } @@ -272,7 +276,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) if (inv != nullptr) { for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { - inv->Statistics()->setChannelMaxPower(c, inverter.MaxChannelPower[c]); + inv->Statistics()->setChannelMaxPower(c, inverter.channel[c].MaxChannelPower); } } diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 2c6f9c8b..35f94d51 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -5,6 +5,7 @@ #include "WebApi_ws_live.h" #include "AsyncJson.h" #include "Configuration.h" +#include "defaults.h" WebApiWsLiveClass::WebApiWsLiveClass() : _ws("/livedata") @@ -102,6 +103,12 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) // Loop all channels for (uint8_t c = 0; c <= inv->Statistics()->getChannelCount(); c++) { + if (c > 0) { + INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial()); + if (inv_cfg != nullptr) { + invObject[String(c)][F("name")]["u"] = inv_cfg->channel[c - 1].Name; + } + } addField(invObject, i, inv, c, FLD_PAC); addField(invObject, i, inv, c, FLD_UAC); addField(invObject, i, inv, c, FLD_IAC); @@ -119,7 +126,9 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) addField(invObject, i, inv, c, FLD_PF); addField(invObject, i, inv, c, FLD_PRA); addField(invObject, i, inv, c, FLD_EFF); - addField(invObject, i, inv, c, FLD_IRR); + if (c > 0 && inv->Statistics()->getChannelMaxPower(c - 1) > 0) { + addField(invObject, i, inv, c, FLD_IRR); + } } if (inv->Statistics()->hasChannelFieldValue(CH0, FLD_EVT_LOG)) { @@ -142,6 +151,16 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) addTotalField(totalObj, "Power", totalPower, "W", 1); addTotalField(totalObj, "YieldDay", totalYieldDay, "Wh", 0); addTotalField(totalObj, "YieldTotal", totalYieldTotal, "kWh", 2); + + JsonObject hintObj = root.createNestedObject("hints"); + struct tm timeinfo; + hintObj[F("time_sync")] = !getLocalTime(&timeinfo, 5); + hintObj[F("radio_problem")] = (!Hoymiles.getRadio()->isConnected() || !Hoymiles.getRadio()->isPVariant()); + if (!strcmp(Configuration.get().Security_Password, ACCESS_POINT_PASSWORD)) { + hintObj[F("default_password")] = true; + } else { + hintObj[F("default_password")] = false; + } } void WebApiWsLiveClass::addField(JsonObject& root, uint8_t idx, std::shared_ptr inv, uint8_t channel, uint8_t fieldId, String topic) diff --git a/src/main.cpp b/src/main.cpp index 0f467474..dcbddae0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -119,7 +119,7 @@ void setup() if (inv != nullptr) { for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { - inv->Statistics()->setChannelMaxPower(c, config.Inverter[i].MaxChannelPower[c]); + inv->Statistics()->setChannelMaxPower(c, config.Inverter[i].channel[c].MaxChannelPower); } } Serial.println(F(" done")); diff --git a/webapp/package.json b/webapp/package.json index cdbe1756..4b676c88 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -27,12 +27,12 @@ "@vitejs/plugin-vue": "^3.2.0", "@vue/eslint-config-typescript": "^11.0.2", "@vue/tsconfig": "^0.1.3", - "eslint": "^8.27.0", + "eslint": "^8.28.0", "eslint-plugin-vue": "^9.7.0", "npm-run-all": "^4.1.5", "sass": "^1.56.1", - "typescript": "^4.8.4", - "vite": "^3.2.3", + "typescript": "^4.9.3", + "vite": "^3.2.4", "vite-plugin-compression": "^0.5.1", "vite-plugin-css-injected-by-js": "^2.1.1", "vue-tsc": "^1.0.9" diff --git a/webapp/src/components/HintView.vue b/webapp/src/components/HintView.vue new file mode 100644 index 00000000..c684a0c8 --- /dev/null +++ b/webapp/src/components/HintView.vue @@ -0,0 +1,50 @@ + + + \ No newline at end of file diff --git a/webapp/src/components/InverterChannelInfo.vue b/webapp/src/components/InverterChannelInfo.vue index e1144dac..dc277910 100644 --- a/webapp/src/components/InverterChannelInfo.vue +++ b/webapp/src/components/InverterChannelInfo.vue @@ -1,6 +1,9 @@