Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development

This commit is contained in:
helgeerbe 2023-12-27 13:11:25 +01:00
commit 367e0f9b6e
11 changed files with 83 additions and 69 deletions

View File

@ -7,7 +7,10 @@
#define CHART_HEIGHT 20 // chart area hight in pixels #define CHART_HEIGHT 20 // chart area hight in pixels
#define CHART_WIDTH 47 // chart area width 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 #define DIAG_POSY 0
class DisplayGraphicDiagramClass { class DisplayGraphicDiagramClass {
@ -15,7 +18,7 @@ public:
DisplayGraphicDiagramClass(); DisplayGraphicDiagramClass();
void init(Scheduler& scheduler, U8G2* display); void init(Scheduler& scheduler, U8G2* display);
void redraw(); void redraw(uint8_t screenSaverOffsetX);
void updatePeriod(); void updatePeriod();
@ -34,6 +37,4 @@ private:
float _iRunningAverage = 0; float _iRunningAverage = 0;
uint16_t _iRunningAverageCnt = 0; uint16_t _iRunningAverageCnt = 0;
};
uint8_t _graphPosX = DIAG_POSX;
};

View File

@ -62,7 +62,7 @@ public:
bool isValidNrf24Config() const; bool isValidNrf24Config() const;
bool isValidCmt2300Config() const; bool isValidCmt2300Config() const;
bool isValidEthConfig() const; bool isValidEthConfig() const;
bool isValidHuaweiConfig(); bool isValidHuaweiConfig() const;
private: private:
PinMapping_t _pinMapping; PinMapping_t _pinMapping;

View File

@ -102,6 +102,9 @@
#define REACHABLE_THRESHOLD 2U #define REACHABLE_THRESHOLD 2U
#define LED_BRIGHTNESS 100U
#define MAX_INVERTER_LIMIT 2250
#define VEDIRECT_ENABLED false #define VEDIRECT_ENABLED false
#define VEDIRECT_VERBOSE_LOGGING false #define VEDIRECT_VERBOSE_LOGGING false
#define VEDIRECT_UPDATESONLY true #define VEDIRECT_UPDATESONLY true

View File

@ -34,8 +34,8 @@ public:
HoymilesRadio_NRF* getRadioNrf(); HoymilesRadio_NRF* getRadioNrf();
HoymilesRadio_CMT* getRadioCmt(); HoymilesRadio_CMT* getRadioCmt();
uint32_t PollInterval() const; uint32_t PollInterval() const const;
void setPollInterval(const uint32_t interval); void setPollInterval(const const uint32_t interval);
void setVerboseLogging(bool verboseLogging); void setVerboseLogging(bool verboseLogging);
bool isAllRadioIdle() const; bool isAllRadioIdle() const;

View File

@ -38,6 +38,7 @@ lib_deps =
olikraus/U8g2 @ ^2.35.8 olikraus/U8g2 @ ^2.35.8
buelowp/sunset @ ^1.1.7 buelowp/sunset @ ^1.1.7
https://github.com/arkhipenko/TaskScheduler#testing https://github.com/arkhipenko/TaskScheduler#testing
https://github.com/arkhipenko/TaskScheduler#testing
https://github.com/coryjfowler/MCP_CAN_lib https://github.com/coryjfowler/MCP_CAN_lib
plerup/EspSoftwareSerial@^8.0.1 plerup/EspSoftwareSerial@^8.0.1
mobizt/FirebaseJson @ ^3.0.6 mobizt/FirebaseJson @ ^3.0.6

View File

@ -161,12 +161,14 @@ void DisplayGraphicClass::loop()
if (Datastore.getIsAtLeastOneReachable()) { if (Datastore.getIsAtLeastOneReachable()) {
displayPowerSave = false; displayPowerSave = false;
if (_isLarge) { if (_isLarge) {
_diagram.redraw(); uint8_t screenSaverOffsetX = enableScreensaver ? (_mExtra % 7) : 0;
_diagram.redraw(screenSaverOffsetX);
} }
if (Datastore.getTotalAcPowerEnabled() > 999) { const float watts = Datastore.getTotalAcPowerEnabled();
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.getTotalAcPowerEnabled() / 1000)); if (watts > 999) {
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], watts / 1000);
} else { } 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); printText(_fmtText, 0);
_previousMillis = millis(); _previousMillis = millis();
@ -224,4 +226,4 @@ void DisplayGraphicClass::setStatus(const bool turnOn)
_displayTurnedOn = turnOn; _displayTurnedOn = turnOn;
} }
DisplayGraphicClass Display; DisplayGraphicClass Display;

View File

@ -37,21 +37,17 @@ void DisplayGraphicDiagramClass::averageLoop()
void DisplayGraphicDiagramClass::dataPointLoop() void DisplayGraphicDiagramClass::dataPointLoop()
{ {
if (_graphValuesCount >= CHART_WIDTH) { if (_graphValuesCount >= std::size(_graphValues)) {
for (uint8_t i = 0; i < CHART_WIDTH - 1; i++) { for (uint8_t i = 0; i < std::size(_graphValues) - 1; i++) {
_graphValues[i] = _graphValues[i + 1]; _graphValues[i] = _graphValues[i + 1];
} }
_graphValuesCount = CHART_WIDTH - 1; _graphValuesCount = std::size(_graphValues) - 1;
} }
if (_iRunningAverageCnt != 0) { if (_iRunningAverageCnt != 0) {
_graphValues[_graphValuesCount++] = _iRunningAverage / _iRunningAverageCnt; _graphValues[_graphValuesCount++] = _iRunningAverage / _iRunningAverageCnt;
_iRunningAverage = 0; _iRunningAverage = 0;
_iRunningAverageCnt = 0; _iRunningAverageCnt = 0;
} }
if (Configuration.get().Display.ScreenSaver) {
_graphPosX = DIAG_POSX - (_graphValuesCount % 2);
}
} }
uint32_t DisplayGraphicDiagramClass::getSecondsPerDot() uint32_t DisplayGraphicDiagramClass::getSecondsPerDot()
@ -64,25 +60,32 @@ void DisplayGraphicDiagramClass::updatePeriod()
_dataPointTask.setInterval(getSecondsPerDot() * TASK_SECOND); _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 // draw diagram axis
_display->drawVLine(_graphPosX, graphPosY, CHART_HEIGHT); _display->drawVLine(graphPosX, graphPosY, CHART_HEIGHT);
_display->drawHLine(_graphPosX, graphPosY + CHART_HEIGHT - 1, CHART_WIDTH); _display->drawHLine(graphPosX, graphPosY + CHART_HEIGHT - 1, CHART_WIDTH);
_display->drawLine(_graphPosX + 1, graphPosY + 1, _graphPosX + 2, graphPosY + 2); // UP-arrow _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 - 2, graphPosY + 2, graphPosX - 1, graphPosY + 1); // UP-arrow
_display->drawLine(_graphPosX + CHART_WIDTH - 3, graphPosY + CHART_HEIGHT + 1, _graphPosX + CHART_WIDTH - 2, graphPosY + CHART_HEIGHT); // LEFT-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 // 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]; char fmtText[7];
const float maxWatts = *std::max_element(_graphValues.begin(), _graphValues.end()); const float maxWatts = *std::max_element(_graphValues.begin(), _graphValues.end());
snprintf(fmtText, sizeof(fmtText), "%dW", static_cast<uint16_t>(maxWatts)); if (maxWatts > 999) {
snprintf(fmtText, sizeof(fmtText), "%2.1fkW", maxWatts / 1000);
} else {
snprintf(fmtText, sizeof(fmtText), "%dW", static_cast<uint16_t>(maxWatts));
}
const uint8_t textLength = strlen(fmtText); 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 // draw chart
const float scaleFactor = maxWatts / CHART_HEIGHT; const float scaleFactor = maxWatts / CHART_HEIGHT;
@ -90,15 +93,15 @@ void DisplayGraphicDiagramClass::redraw()
for (int i = 0; i < _graphValuesCount; i++) { for (int i = 0; i < _graphValuesCount; i++) {
if (scaleFactor > 0) { if (scaleFactor > 0) {
if (i == 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 { } 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 // draw one tick per hour to the x-axis
if (i * getSecondsPerDot() > (3600u * axisTick)) { if (i * getSecondsPerDot() > (3600u * axisTick)) {
_display->drawPixel(_graphPosX + 1 + i, graphPosY + CHART_HEIGHT); _display->drawPixel(graphPosX + 1 + i, graphPosY + CHART_HEIGHT);
axisTick++; axisTick++;
} }
} }

View File

@ -92,22 +92,22 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
} }
JsonObject victronPinObj = curPin.createNestedObject("victron"); JsonObject victronPinObj = curPin.createNestedObject("victron");
victronPinObj[F("rx")] = pin.victron_rx; victronPinObj["rx"] = pin.victron_rx;
victronPinObj[F("tx")] = pin.victron_tx; victronPinObj["tx"] = pin.victron_tx;
JsonObject batteryPinObj = curPin.createNestedObject("battery"); JsonObject batteryPinObj = curPin.createNestedObject("battery");
batteryPinObj[F("rx")] = pin.battery_rx; batteryPinObj["rx"] = pin.battery_rx;
batteryPinObj[F("rxen")] = pin.battery_rxen; batteryPinObj["rxen"] = pin.battery_rxen;
batteryPinObj[F("tx")] = pin.battery_tx; batteryPinObj["tx"] = pin.battery_tx;
batteryPinObj[F("txen")] = pin.battery_txen; batteryPinObj["txen"] = pin.battery_txen;
JsonObject huaweiPinObj = curPin.createNestedObject("huawei"); JsonObject huaweiPinObj = curPin.createNestedObject("huawei");
huaweiPinObj[F("miso")] = pin.huawei_miso; huaweiPinObj["miso"] = pin.huawei_miso;
huaweiPinObj[F("mosi")] = pin.huawei_mosi; huaweiPinObj["mosi"] = pin.huawei_mosi;
huaweiPinObj[F("clk")] = pin.huawei_clk; huaweiPinObj["clk"] = pin.huawei_clk;
huaweiPinObj[F("irq")] = pin.huawei_irq; huaweiPinObj["irq"] = pin.huawei_irq;
huaweiPinObj[F("cs")] = pin.huawei_cs; huaweiPinObj["cs"] = pin.huawei_cs;
huaweiPinObj[F("power")] = pin.huawei_power; huaweiPinObj["power"] = pin.huawei_power;
response->setLength(); response->setLength();
request->send(response); request->send(response);

View File

@ -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_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"); 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) void WebApiWebappClass::init(AsyncWebServer& server)
{ {
_server = &server; _server = &server;
@ -62,12 +66,12 @@ void WebApiWebappClass::init(AsyncWebServer& server)
}); });
_server->on("/js/app.js", HTTP_GET, [](AsyncWebServerRequest* request) { _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 // check client If-None-Match header vs ETag/AUTO_GIT_HASH
bool eTagMatch = false; bool eTagMatch = false;
if (request->hasHeader("If-None-Match")) { if (request->hasHeader("If-None-Match")) {
const AsyncWebHeader* h = request->getHeader("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; eTagMatch = true;
} }
} }
@ -82,7 +86,7 @@ void WebApiWebappClass::init(AsyncWebServer& server)
} }
// HTTP requires cache headers in 200 and 304 to be identical // HTTP requires cache headers in 200 and 304 to be identical
response->addHeader("Cache-Control", "public, must-revalidate"); response->addHeader("Cache-Control", "public, must-revalidate");
response->addHeader("ETag", AUTO_GIT_HASH); response->addHeader("ETag", ETAG_HTTP_HEADER_VAL);
#else #else
AsyncWebServerResponse* response = request->beginResponse_P(200, "text/javascript", file_app_js_start, file_app_js_end - file_app_js_start); 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"); response->addHeader("Content-Encoding", "gzip");
@ -93,4 +97,4 @@ void WebApiWebappClass::init(AsyncWebServer& server)
void WebApiWebappClass::loop() void WebApiWebappClass::loop()
{ {
} }

View File

@ -23,7 +23,7 @@
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "^1.6.0", "@intlify/unplugin-vue-i18n": "^2.0.0",
"@rushstack/eslint-patch": "^1.6.1", "@rushstack/eslint-patch": "^1.6.1",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/bootstrap": "^5.2.10", "@types/bootstrap": "^5.2.10",
@ -41,7 +41,7 @@
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.0.10", "vite": "^5.0.10",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-css-injected-by-js": "^3.3.0", "vite-plugin-css-injected-by-js": "^3.3.1",
"vue-tsc": "^1.8.25" "vue-tsc": "^1.8.26"
} }
} }

View File

@ -245,10 +245,10 @@
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.8.0.tgz#62adf8f6ef67c8eba6cf8d521e248f3503f237d3" resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.8.0.tgz#62adf8f6ef67c8eba6cf8d521e248f3503f237d3"
integrity sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ== integrity sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ==
"@intlify/unplugin-vue-i18n@^1.6.0": "@intlify/unplugin-vue-i18n@^2.0.0":
version "1.6.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-1.6.0.tgz#49ab21125c79257e455a6e562a2511c8681b870c" resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-2.0.0.tgz#5b087e17b4eb4381d0a111cd89df4037880e932f"
integrity sha512-IGeFNWxdEvB12E/3Y/+nmIsGeTg5okPsK1XEtUUD/DdkHbVqUbJucMpHKeHF8Px55Qca551pQCs/g+VjNUt6KA== integrity sha512-1oKvm92L9l2od2H9wKx2ZvR4tzn7gUtd7bPLI7AWUmm7U9H1iEypndt5d985ypxGsEs0gToDaKTrytbBIJwwSg==
dependencies: dependencies:
"@intlify/bundle-utils" "^7.4.0" "@intlify/bundle-utils" "^7.4.0"
"@intlify/shared" "^9.4.0" "@intlify/shared" "^9.4.0"
@ -690,10 +690,10 @@
"@typescript-eslint/parser" "^6.7.0" "@typescript-eslint/parser" "^6.7.0"
vue-eslint-parser "^9.3.1" vue-eslint-parser "^9.3.1"
"@vue/language-core@1.8.25": "@vue/language-core@1.8.26":
version "1.8.25" version "1.8.26"
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.25.tgz#b44b4e3c244ba9b1b79cccf9eb7b046535a4676f" resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.26.tgz#7edb6b51a6ed57b618928500c3cbda9757a9f5f0"
integrity sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA== integrity sha512-9cmza/Y2YTiOnKZ0Mi9zsNn7Irw+aKirP+5LLWVSNaL3fjKJjW1cD3HGBckasY2RuVh4YycvdA9/Q6EBpVd/7Q==
dependencies: dependencies:
"@volar/language-core" "~1.11.1" "@volar/language-core" "~1.11.1"
"@volar/source-map" "~1.11.1" "@volar/source-map" "~1.11.1"
@ -2552,10 +2552,10 @@ vite-plugin-compression@^0.5.1:
debug "^4.3.3" debug "^4.3.3"
fs-extra "^10.0.0" fs-extra "^10.0.0"
vite-plugin-css-injected-by-js@^3.3.0: vite-plugin-css-injected-by-js@^3.3.1:
version "3.3.0" version "3.3.1"
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.0.tgz#c19480a9e42a95c5bced976a9dde1446f9bd91ff" resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.1.tgz#26b41f108c5554ee728359bdec01c68c93a48547"
integrity sha512-xG+jyHNCmUqi/TXp6q88wTJGeAOrNLSyUUTp4qEQ9QZLGcHWQQsCsSSKa59rPMQr8sOzfzmWDd8enGqfH/dBew== integrity sha512-PjM/X45DR3/V1K1fTRs8HtZHEQ55kIfdrn+dzaqNBFrOYO073SeSNCxp4j7gSYhV9NffVHaEnOL4myoko0ePAg==
vite@^5.0.10: vite@^5.0.10:
version "5.0.10" version "5.0.10"
@ -2605,13 +2605,13 @@ vue-template-compiler@^2.7.14:
de-indent "^1.0.2" de-indent "^1.0.2"
he "^1.2.0" he "^1.2.0"
vue-tsc@^1.8.25: vue-tsc@^1.8.26:
version "1.8.25" version "1.8.26"
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.8.25.tgz#90cd03e71d28c5c4a8068167b232eb97cc96b77f" resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.8.26.tgz#f66abd1dab4e4593590b2b7d4ede0a696882feec"
integrity sha512-lHsRhDc/Y7LINvYhZ3pv4elflFADoEOo67vfClAfF2heVHpHmVquLSjojgCSIwzA4F0Pc4vowT/psXCYcfk+iQ== integrity sha512-jMEJ4aqU/l1hdgmeExH5h1TFoN+hbho0A2ZAhHy53/947DGm7Qj/bpB85VpECOCwV00h7JYNVnvoD2ceOorB4Q==
dependencies: dependencies:
"@volar/typescript" "~1.11.1" "@volar/typescript" "~1.11.1"
"@vue/language-core" "1.8.25" "@vue/language-core" "1.8.26"
semver "^7.5.4" semver "^7.5.4"
vue@^3.3.13: vue@^3.3.13: