diff --git a/include/Display_Graphic_Diagram.h b/include/Display_Graphic_Diagram.h index 0626436c..2067a3f0 100644 --- a/include/Display_Graphic_Diagram.h +++ b/include/Display_Graphic_Diagram.h @@ -7,7 +7,10 @@ #define CHART_HEIGHT 20 // chart area hight in pixels #define CHART_WIDTH 47 // chart area width in pixels -#define DIAG_POSX 80 // position were Diag is drawn at + +// Left-Upper position of diagram is drawn +// (text of Y-axis is display left of that pos) +#define DIAG_POSX 80 #define DIAG_POSY 0 class DisplayGraphicDiagramClass { @@ -15,7 +18,7 @@ public: DisplayGraphicDiagramClass(); void init(Scheduler& scheduler, U8G2* display); - void redraw(); + void redraw(uint8_t screenSaverOffsetX); void updatePeriod(); @@ -34,6 +37,4 @@ private: float _iRunningAverage = 0; uint16_t _iRunningAverageCnt = 0; - - uint8_t _graphPosX = DIAG_POSX; -}; \ No newline at end of file +}; diff --git a/include/PinMapping.h b/include/PinMapping.h index 30c56402..4096e807 100644 --- a/include/PinMapping.h +++ b/include/PinMapping.h @@ -62,7 +62,7 @@ public: bool isValidNrf24Config() const; bool isValidCmt2300Config() const; bool isValidEthConfig() const; - bool isValidHuaweiConfig(); + bool isValidHuaweiConfig() const; private: PinMapping_t _pinMapping; diff --git a/include/defaults.h b/include/defaults.h index 9ec38406..cb8c322b 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -102,6 +102,9 @@ #define REACHABLE_THRESHOLD 2U +#define LED_BRIGHTNESS 100U + +#define MAX_INVERTER_LIMIT 2250 #define VEDIRECT_ENABLED false #define VEDIRECT_VERBOSE_LOGGING false #define VEDIRECT_UPDATESONLY true diff --git a/lib/Hoymiles/src/Hoymiles.h b/lib/Hoymiles/src/Hoymiles.h index 86a7d6ca..e6352838 100644 --- a/lib/Hoymiles/src/Hoymiles.h +++ b/lib/Hoymiles/src/Hoymiles.h @@ -34,8 +34,8 @@ public: HoymilesRadio_NRF* getRadioNrf(); HoymilesRadio_CMT* getRadioCmt(); - uint32_t PollInterval() const; - void setPollInterval(const uint32_t interval); + uint32_t PollInterval() const const; + void setPollInterval(const const uint32_t interval); void setVerboseLogging(bool verboseLogging); bool isAllRadioIdle() const; diff --git a/platformio.ini b/platformio.ini index 4a5478d9..16734b11 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,6 +38,7 @@ lib_deps = olikraus/U8g2 @ ^2.35.8 buelowp/sunset @ ^1.1.7 https://github.com/arkhipenko/TaskScheduler#testing + https://github.com/arkhipenko/TaskScheduler#testing https://github.com/coryjfowler/MCP_CAN_lib plerup/EspSoftwareSerial@^8.0.1 mobizt/FirebaseJson @ ^3.0.6 diff --git a/src/Display_Graphic.cpp b/src/Display_Graphic.cpp index 5a06452f..94a0de84 100644 --- a/src/Display_Graphic.cpp +++ b/src/Display_Graphic.cpp @@ -161,12 +161,14 @@ void DisplayGraphicClass::loop() if (Datastore.getIsAtLeastOneReachable()) { displayPowerSave = false; if (_isLarge) { - _diagram.redraw(); + uint8_t screenSaverOffsetX = enableScreensaver ? (_mExtra % 7) : 0; + _diagram.redraw(screenSaverOffsetX); } - if (Datastore.getTotalAcPowerEnabled() > 999) { - snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.getTotalAcPowerEnabled() / 1000)); + const float watts = Datastore.getTotalAcPowerEnabled(); + if (watts > 999) { + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], watts / 1000); } else { - snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], Datastore.getTotalAcPowerEnabled()); + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], watts); } printText(_fmtText, 0); _previousMillis = millis(); @@ -224,4 +226,4 @@ void DisplayGraphicClass::setStatus(const bool turnOn) _displayTurnedOn = turnOn; } -DisplayGraphicClass Display; \ No newline at end of file +DisplayGraphicClass Display; diff --git a/src/Display_Graphic_Diagram.cpp b/src/Display_Graphic_Diagram.cpp index 4a98c169..489f295f 100644 --- a/src/Display_Graphic_Diagram.cpp +++ b/src/Display_Graphic_Diagram.cpp @@ -37,21 +37,17 @@ void DisplayGraphicDiagramClass::averageLoop() void DisplayGraphicDiagramClass::dataPointLoop() { - if (_graphValuesCount >= CHART_WIDTH) { - for (uint8_t i = 0; i < CHART_WIDTH - 1; i++) { + if (_graphValuesCount >= std::size(_graphValues)) { + for (uint8_t i = 0; i < std::size(_graphValues) - 1; i++) { _graphValues[i] = _graphValues[i + 1]; } - _graphValuesCount = CHART_WIDTH - 1; + _graphValuesCount = std::size(_graphValues) - 1; } if (_iRunningAverageCnt != 0) { _graphValues[_graphValuesCount++] = _iRunningAverage / _iRunningAverageCnt; _iRunningAverage = 0; _iRunningAverageCnt = 0; } - - if (Configuration.get().Display.ScreenSaver) { - _graphPosX = DIAG_POSX - (_graphValuesCount % 2); - } } uint32_t DisplayGraphicDiagramClass::getSecondsPerDot() @@ -64,25 +60,32 @@ void DisplayGraphicDiagramClass::updatePeriod() _dataPointTask.setInterval(getSecondsPerDot() * TASK_SECOND); } -void DisplayGraphicDiagramClass::redraw() +void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX) { - uint8_t graphPosY = DIAG_POSY; + const uint8_t graphPosX = DIAG_POSX + ((screenSaverOffsetX > 3) ? 1 : 0); // screenSaverOffsetX expected to be in range 0..6 + const uint8_t graphPosY = DIAG_POSY + ((screenSaverOffsetX > 3) ? 1 : 0); // draw diagram axis - _display->drawVLine(_graphPosX, graphPosY, CHART_HEIGHT); - _display->drawHLine(_graphPosX, graphPosY + CHART_HEIGHT - 1, CHART_WIDTH); + _display->drawVLine(graphPosX, graphPosY, CHART_HEIGHT); + _display->drawHLine(graphPosX, graphPosY + CHART_HEIGHT - 1, CHART_WIDTH); - _display->drawLine(_graphPosX + 1, graphPosY + 1, _graphPosX + 2, graphPosY + 2); // UP-arrow - _display->drawLine(_graphPosX + CHART_WIDTH - 3, graphPosY + CHART_HEIGHT - 3, _graphPosX + CHART_WIDTH - 2, graphPosY + CHART_HEIGHT - 2); // LEFT-arrow - _display->drawLine(_graphPosX + CHART_WIDTH - 3, graphPosY + CHART_HEIGHT + 1, _graphPosX + CHART_WIDTH - 2, graphPosY + CHART_HEIGHT); // LEFT-arrow + _display->drawLine(graphPosX + 1, graphPosY + 1, graphPosX + 2, graphPosY + 2); // UP-arrow + _display->drawLine(graphPosX - 2, graphPosY + 2, graphPosX - 1, graphPosY + 1); // UP-arrow + _display->drawLine(graphPosX + CHART_WIDTH - 3, graphPosY + CHART_HEIGHT - 3, graphPosX + CHART_WIDTH - 2, graphPosY + CHART_HEIGHT - 2); // LEFT-arrow + _display->drawLine(graphPosX + CHART_WIDTH - 3, graphPosY + CHART_HEIGHT + 1, graphPosX + CHART_WIDTH - 2, graphPosY + CHART_HEIGHT); // LEFT-arrow // draw AC value - _display->setFont(u8g2_font_tom_thumb_4x6_mr); + _display->setFont(u8g2_font_tom_thumb_4x6_mr); // 4 pixels per char char fmtText[7]; const float maxWatts = *std::max_element(_graphValues.begin(), _graphValues.end()); - snprintf(fmtText, sizeof(fmtText), "%dW", static_cast(maxWatts)); + if (maxWatts > 999) { + snprintf(fmtText, sizeof(fmtText), "%2.1fkW", maxWatts / 1000); + } else { + snprintf(fmtText, sizeof(fmtText), "%dW", static_cast(maxWatts)); + } const uint8_t textLength = strlen(fmtText); - _display->drawStr(_graphPosX - (textLength * 4), graphPosY + 5, fmtText); + const uint8_t space_and_arrow_pixels = 2; + _display->drawStr(graphPosX - space_and_arrow_pixels - (textLength * 4), graphPosY + 5, fmtText); // draw chart const float scaleFactor = maxWatts / CHART_HEIGHT; @@ -90,15 +93,15 @@ void DisplayGraphicDiagramClass::redraw() for (int i = 0; i < _graphValuesCount; i++) { if (scaleFactor > 0) { if (i == 0) { - _display->drawPixel(_graphPosX + 1 + i, graphPosY + CHART_HEIGHT - ((_graphValues[i] / scaleFactor) + 0.5)); // + 0.5 to round mathematical + _display->drawPixel(graphPosX + 1 + i, graphPosY + CHART_HEIGHT - ((_graphValues[i] / scaleFactor) + 0.5)); // + 0.5 to round mathematical } else { - _display->drawLine(_graphPosX + i, graphPosY + CHART_HEIGHT - ((_graphValues[i - 1] / scaleFactor) + 0.5), _graphPosX + 1 + i, graphPosY + CHART_HEIGHT - ((_graphValues[i] / scaleFactor) + 0.5)); + _display->drawLine(graphPosX + i, graphPosY + CHART_HEIGHT - ((_graphValues[i - 1] / scaleFactor) + 0.5), graphPosX + 1 + i, graphPosY + CHART_HEIGHT - ((_graphValues[i] / scaleFactor) + 0.5)); } } // draw one tick per hour to the x-axis if (i * getSecondsPerDot() > (3600u * axisTick)) { - _display->drawPixel(_graphPosX + 1 + i, graphPosY + CHART_HEIGHT); + _display->drawPixel(graphPosX + 1 + i, graphPosY + CHART_HEIGHT); axisTick++; } } diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index 29508cc9..8379b156 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -92,22 +92,22 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) } JsonObject victronPinObj = curPin.createNestedObject("victron"); - victronPinObj[F("rx")] = pin.victron_rx; - victronPinObj[F("tx")] = pin.victron_tx; + victronPinObj["rx"] = pin.victron_rx; + victronPinObj["tx"] = pin.victron_tx; JsonObject batteryPinObj = curPin.createNestedObject("battery"); - batteryPinObj[F("rx")] = pin.battery_rx; - batteryPinObj[F("rxen")] = pin.battery_rxen; - batteryPinObj[F("tx")] = pin.battery_tx; - batteryPinObj[F("txen")] = pin.battery_txen; + batteryPinObj["rx"] = pin.battery_rx; + batteryPinObj["rxen"] = pin.battery_rxen; + batteryPinObj["tx"] = pin.battery_tx; + batteryPinObj["txen"] = pin.battery_txen; JsonObject huaweiPinObj = curPin.createNestedObject("huawei"); - huaweiPinObj[F("miso")] = pin.huawei_miso; - huaweiPinObj[F("mosi")] = pin.huawei_mosi; - huaweiPinObj[F("clk")] = pin.huawei_clk; - huaweiPinObj[F("irq")] = pin.huawei_irq; - huaweiPinObj[F("cs")] = pin.huawei_cs; - huaweiPinObj[F("power")] = pin.huawei_power; + huaweiPinObj["miso"] = pin.huawei_miso; + huaweiPinObj["mosi"] = pin.huawei_mosi; + huaweiPinObj["clk"] = pin.huawei_clk; + huaweiPinObj["irq"] = pin.huawei_irq; + huaweiPinObj["cs"] = pin.huawei_cs; + huaweiPinObj["power"] = pin.huawei_power; response->setLength(); request->send(response); diff --git a/src/WebApi_webapp.cpp b/src/WebApi_webapp.cpp index 260566fa..ea185b2d 100644 --- a/src/WebApi_webapp.cpp +++ b/src/WebApi_webapp.cpp @@ -18,6 +18,10 @@ extern const uint8_t file_zones_json_end[] asm("_binary_webapp_dist_zones_json_g extern const uint8_t file_app_js_end[] asm("_binary_webapp_dist_js_app_js_gz_end"); extern const uint8_t file_site_webmanifest_end[] asm("_binary_webapp_dist_site_webmanifest_end"); +#ifdef AUTO_GIT_HASH +#define ETAG_HTTP_HEADER_VAL "\"" AUTO_GIT_HASH "\"" // ETag value must be between quotes +#endif + void WebApiWebappClass::init(AsyncWebServer& server) { _server = &server; @@ -62,12 +66,12 @@ void WebApiWebappClass::init(AsyncWebServer& server) }); _server->on("/js/app.js", HTTP_GET, [](AsyncWebServerRequest* request) { -#ifdef AUTO_GIT_HASH +#ifdef ETAG_HTTP_HEADER_VAL // check client If-None-Match header vs ETag/AUTO_GIT_HASH bool eTagMatch = false; if (request->hasHeader("If-None-Match")) { const AsyncWebHeader* h = request->getHeader("If-None-Match"); - if (strncmp(AUTO_GIT_HASH, h->value().c_str(), strlen(AUTO_GIT_HASH)) == 0) { + if (strncmp(ETAG_HTTP_HEADER_VAL, h->value().c_str(), strlen(ETAG_HTTP_HEADER_VAL)) == 0) { eTagMatch = true; } } @@ -82,7 +86,7 @@ void WebApiWebappClass::init(AsyncWebServer& server) } // HTTP requires cache headers in 200 and 304 to be identical response->addHeader("Cache-Control", "public, must-revalidate"); - response->addHeader("ETag", AUTO_GIT_HASH); + response->addHeader("ETag", ETAG_HTTP_HEADER_VAL); #else AsyncWebServerResponse* response = request->beginResponse_P(200, "text/javascript", file_app_js_start, file_app_js_end - file_app_js_start); response->addHeader("Content-Encoding", "gzip"); @@ -93,4 +97,4 @@ void WebApiWebappClass::init(AsyncWebServer& server) void WebApiWebappClass::loop() { -} \ No newline at end of file +} diff --git a/webapp/package.json b/webapp/package.json index c5050c46..60e57e94 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -23,7 +23,7 @@ "vue-router": "^4.2.5" }, "devDependencies": { - "@intlify/unplugin-vue-i18n": "^1.6.0", + "@intlify/unplugin-vue-i18n": "^2.0.0", "@rushstack/eslint-patch": "^1.6.1", "@tsconfig/node18": "^18.2.2", "@types/bootstrap": "^5.2.10", @@ -41,7 +41,7 @@ "typescript": "^5.3.3", "vite": "^5.0.10", "vite-plugin-compression": "^0.5.1", - "vite-plugin-css-injected-by-js": "^3.3.0", - "vue-tsc": "^1.8.25" + "vite-plugin-css-injected-by-js": "^3.3.1", + "vue-tsc": "^1.8.26" } } diff --git a/webapp/yarn.lock b/webapp/yarn.lock index b154652d..b3b54b48 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -245,10 +245,10 @@ resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.8.0.tgz#62adf8f6ef67c8eba6cf8d521e248f3503f237d3" integrity sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ== -"@intlify/unplugin-vue-i18n@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-1.6.0.tgz#49ab21125c79257e455a6e562a2511c8681b870c" - integrity sha512-IGeFNWxdEvB12E/3Y/+nmIsGeTg5okPsK1XEtUUD/DdkHbVqUbJucMpHKeHF8Px55Qca551pQCs/g+VjNUt6KA== +"@intlify/unplugin-vue-i18n@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-2.0.0.tgz#5b087e17b4eb4381d0a111cd89df4037880e932f" + integrity sha512-1oKvm92L9l2od2H9wKx2ZvR4tzn7gUtd7bPLI7AWUmm7U9H1iEypndt5d985ypxGsEs0gToDaKTrytbBIJwwSg== dependencies: "@intlify/bundle-utils" "^7.4.0" "@intlify/shared" "^9.4.0" @@ -690,10 +690,10 @@ "@typescript-eslint/parser" "^6.7.0" vue-eslint-parser "^9.3.1" -"@vue/language-core@1.8.25": - version "1.8.25" - resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.25.tgz#b44b4e3c244ba9b1b79cccf9eb7b046535a4676f" - integrity sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA== +"@vue/language-core@1.8.26": + version "1.8.26" + resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.26.tgz#7edb6b51a6ed57b618928500c3cbda9757a9f5f0" + integrity sha512-9cmza/Y2YTiOnKZ0Mi9zsNn7Irw+aKirP+5LLWVSNaL3fjKJjW1cD3HGBckasY2RuVh4YycvdA9/Q6EBpVd/7Q== dependencies: "@volar/language-core" "~1.11.1" "@volar/source-map" "~1.11.1" @@ -2552,10 +2552,10 @@ vite-plugin-compression@^0.5.1: debug "^4.3.3" fs-extra "^10.0.0" -vite-plugin-css-injected-by-js@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.0.tgz#c19480a9e42a95c5bced976a9dde1446f9bd91ff" - integrity sha512-xG+jyHNCmUqi/TXp6q88wTJGeAOrNLSyUUTp4qEQ9QZLGcHWQQsCsSSKa59rPMQr8sOzfzmWDd8enGqfH/dBew== +vite-plugin-css-injected-by-js@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.1.tgz#26b41f108c5554ee728359bdec01c68c93a48547" + integrity sha512-PjM/X45DR3/V1K1fTRs8HtZHEQ55kIfdrn+dzaqNBFrOYO073SeSNCxp4j7gSYhV9NffVHaEnOL4myoko0ePAg== vite@^5.0.10: version "5.0.10" @@ -2605,13 +2605,13 @@ vue-template-compiler@^2.7.14: de-indent "^1.0.2" he "^1.2.0" -vue-tsc@^1.8.25: - version "1.8.25" - resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.8.25.tgz#90cd03e71d28c5c4a8068167b232eb97cc96b77f" - integrity sha512-lHsRhDc/Y7LINvYhZ3pv4elflFADoEOo67vfClAfF2heVHpHmVquLSjojgCSIwzA4F0Pc4vowT/psXCYcfk+iQ== +vue-tsc@^1.8.26: + version "1.8.26" + resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.8.26.tgz#f66abd1dab4e4593590b2b7d4ede0a696882feec" + integrity sha512-jMEJ4aqU/l1hdgmeExH5h1TFoN+hbho0A2ZAhHy53/947DGm7Qj/bpB85VpECOCwV00h7JYNVnvoD2ceOorB4Q== dependencies: "@volar/typescript" "~1.11.1" - "@vue/language-core" "1.8.25" + "@vue/language-core" "1.8.26" semver "^7.5.4" vue@^3.3.13: