Browse Source

Merge branch 'development03' into zero-export

pull/1475/head
lumapu 11 months ago
parent
commit
053a2e0079
  1. 32
      .github/workflows/compile_development.yml
  2. 22
      scripts/buildManifest.py
  3. 23
      src/CHANGES.md
  4. 2
      src/app.cpp
  5. 15
      src/config/settings.h
  6. 4
      src/defines.h
  7. 4
      src/hm/Communication.h
  8. 15
      src/hm/Heuristic.h
  9. 50
      src/hm/hmInverter.h
  10. 13
      src/hm/hmSystem.h
  11. 2
      src/publisher/pubMqtt.h
  12. 4
      src/publisher/pubMqttIvData.h
  13. 4
      src/utils/scheduler.h
  14. 55
      src/web/RestApi.h
  15. 1
      src/web/html/history.html
  16. 6
      src/web/html/includes/nav.html
  17. 4
      src/web/html/index.html
  18. 6
      src/web/html/serial.html
  19. 9
      src/web/html/setup.html
  20. 2
      src/web/html/style.css
  21. 37
      src/web/lang.json
  22. 3
      src/web/web.h

32
.github/workflows/compile_development.yml

@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
build-en: build-en:
name: Build Environments (English) name: Build (EN)
needs: check needs: check
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
@ -77,7 +77,7 @@ jobs:
path: firmware/* path: firmware/*
build-de: build-de:
name: Build Environments (German) name: Build (DE)
needs: check needs: check
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
@ -138,7 +138,7 @@ jobs:
path: firmware/* path: firmware/*
deploy: deploy:
name: Deploy Environments name: Update Artifacts / Deploy
needs: [build-en, build-de] needs: [build-en, build-de]
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: false continue-on-error: false
@ -164,9 +164,35 @@ jobs:
env: env:
VERSION: ${{ steps.version_name.outputs.name }} VERSION: ${{ steps.version_name.outputs.name }}
- name: Create ESP Web Tools Manifest
working-directory: src
run: python ../scripts/buildManifest.py
- name: Copy install html
run: mv scripts/gh-action-dev-build-flash.html firmware/install.html
- name: Copy Changes.md
run: mv src/CHANGES.md firmware/CHANGES.md
- name: Rename firmware directory - name: Rename firmware directory
run: mv firmware ${{ steps.version_name.outputs.name }} run: mv firmware ${{ steps.version_name.outputs.name }}
- name: delete environment Artifacts
uses: geekyeggo/delete-artifact@v4
with:
name: dev-*
- name: Create Artifact
uses: actions/upload-artifact@v4
with:
name: dev-${{ steps.version_name.outputs.name }}
path: |
${{ steps.version_name.outputs.name }}/*
manual/User_Manual.md
manual/Getting_Started.md
- name: Deploy - name: Deploy
uses: nogsantos/scp-deploy@master uses: nogsantos/scp-deploy@master
with: with:

22
scripts/buildManifest.py

@ -36,9 +36,27 @@ def buildManifest(path, infile, outfile):
esp32["parts"].append({"path": "ESP32/bootloader.bin", "offset": 4096}) esp32["parts"].append({"path": "ESP32/bootloader.bin", "offset": 4096})
esp32["parts"].append({"path": "ESP32/partitions.bin", "offset": 32768}) esp32["parts"].append({"path": "ESP32/partitions.bin", "offset": 32768})
esp32["parts"].append({"path": "ESP32/ota.bin", "offset": 57344}) esp32["parts"].append({"path": "ESP32/ota.bin", "offset": 57344})
esp32["parts"].append({"path": "ESP32/" + version[1] + "_" + sha + "_esp32.bin", "offset": 65536}) esp32["parts"].append({"path": "ESP32/" + version[1] + "_" + sha + "_esp32-wroom32.bin", "offset": 65536})
data["builds"].append(esp32) data["builds"].append(esp32)
esp32s2 = {}
esp32s2["chipFamily"] = "ESP32-S2"
esp32s2["parts"] = []
esp32s2["parts"].append({"path": "ESP32-S2/bootloader.bin", "offset": 4096})
esp32s2["parts"].append({"path": "ESP32-S2/partitions.bin", "offset": 32768})
esp32s2["parts"].append({"path": "ESP32-S2/ota.bin", "offset": 57344})
esp32s2["parts"].append({"path": "ESP32-S2/" + version[1] + "_" + sha + "_esp32-s2-mini.bin", "offset": 65536})
data["builds"].append(esp32s2)
esp32s3 = {}
esp32s3["chipFamily"] = "ESP32-S3"
esp32s3["parts"] = []
esp32s3["parts"].append({"path": "ESP32/bootloader.bin", "offset": 4096})
esp32s3["parts"].append({"path": "ESP32/partitions.bin", "offset": 32768})
esp32s3["parts"].append({"path": "ESP32/ota.bin", "offset": 57344})
esp32s3["parts"].append({"path": "ESP32-S3/" + version[1] + "_" + sha + "_opendtufusion.bin", "offset": 65536})
data["builds"].append(esp32s3)
esp8266 = {} esp8266 = {}
esp8266["chipFamily"] = "ESP8266" esp8266["chipFamily"] = "ESP8266"
esp8266["parts"] = [] esp8266["parts"] = []
@ -47,7 +65,7 @@ def buildManifest(path, infile, outfile):
jsonString = json.dumps(data, indent=2) jsonString = json.dumps(data, indent=2)
fp = open(path + "firmware/" + outfile, "w") fp = open(path + "../firmware/" + outfile, "w")
fp.write(jsonString) fp.write(jsonString)
fp.close() fp.close()

23
src/CHANGES.md

@ -1,5 +1,24 @@
# Development Changes # Development Changes
## 0.8.89 - 2024-03-02
* merge PR: Collection of small fixes #1465
* fix: show esp type on `/history` #1463
* improved HMS-400-1T support (serial number 1125...) #1460
## 0.8.88 - 2024-02-28
* fix MqTT statistic data overflow #1458
* add HMS-400-1T support (serial number 1125...) #1460
* removed `yield efficiency` because the inverter already calculates correct #1243
* merge PR: Remove hint to INV_RESET_MIDNIGHT resp. INV_PAUSE_DURING_NIGHT #1431
## 0.8.87 - 2024-02-25
* fix translations #1455 #1442
## 0.8.86 - 2024-02-23
* RestAPI check for parent element to be JsonObject #1449
* fix translation #1448 #1442
* fix reset values when inverter status is 'not available' #1035 #1437
## 0.8.85 - 2024-02-22 ## 0.8.85 - 2024-02-22
* possible fix of MqTT fix "total values are sent to often" #1421 * possible fix of MqTT fix "total values are sent to often" #1421
* fix translation #1442 * fix translation #1442
@ -251,7 +270,7 @@
## 0.8.39 - 2024-01-01 ## 0.8.39 - 2024-01-01
* fix MqTT dis_night_comm in the morning #1309 #1286 * fix MqTT dis_night_comm in the morning #1309 #1286
* seperated offset for sunrise and sunset #1308 * separated offset for sunrise and sunset #1308
* powerlimit (active power control) now has one decimal place (MqTT / API) #1199 * powerlimit (active power control) now has one decimal place (MqTT / API) #1199
* merge Prometheus metrics fix #1310 * merge Prometheus metrics fix #1310
* merge MI grid profile request #1306 * merge MI grid profile request #1306
@ -464,7 +483,7 @@
## 0.7.61 - 2023-10-01 ## 0.7.61 - 2023-10-01
* merged `hmPayload` and `hmsPayload` into single class * merged `hmPayload` and `hmsPayload` into single class
* merged generic radio functions into new parent class `radio.h` * merged generic radio functions into new parent class `radio.h`
* moved radio statistics into the inverter - each inverter has now seperate statistics which can be accessed by click on the footer in `/live` * moved radio statistics into the inverter - each inverter has now separate statistics which can be accessed by click on the footer in `/live`
* fix compiler warnings #1191 * fix compiler warnings #1191
* fix ePaper logo during night time #1151 * fix ePaper logo during night time #1151

2
src/app.cpp

@ -484,7 +484,7 @@ void app:: zeroIvValues(bool checkAvail, bool skipYieldDay) {
continue; // skip to next inverter continue; // skip to next inverter
if (checkAvail) { if (checkAvail) {
if (!iv->isAvailable()) if (iv->isAvailable())
continue; continue;
} }

15
src/config/settings.h

@ -170,7 +170,7 @@ typedef struct {
} cfgIv_t; } cfgIv_t;
typedef struct { typedef struct {
bool enabled; // bool enabled;
cfgIv_t iv[MAX_NUM_INVERTERS]; cfgIv_t iv[MAX_NUM_INVERTERS];
uint16_t sendInterval; uint16_t sendInterval;
@ -179,7 +179,6 @@ typedef struct {
bool rstValsCommStop; bool rstValsCommStop;
bool rstMaxValsMidNight; bool rstMaxValsMidNight;
bool startWithoutTime; bool startWithoutTime;
float yieldEffiency;
bool readGrid; bool readGrid;
} cfgInst_t; } cfgInst_t;
@ -495,7 +494,6 @@ class settings {
mCfg.inst.rstValsCommStop = false; mCfg.inst.rstValsCommStop = false;
mCfg.inst.startWithoutTime = false; mCfg.inst.startWithoutTime = false;
mCfg.inst.rstMaxValsMidNight = false; mCfg.inst.rstMaxValsMidNight = false;
mCfg.inst.yieldEffiency = 1.0f;
mCfg.inst.readGrid = true; mCfg.inst.readGrid = true;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
@ -840,30 +838,23 @@ class settings {
void jsonInst(JsonObject obj, bool set = false) { void jsonInst(JsonObject obj, bool set = false) {
if(set) { if(set) {
obj[F("intvl")] = mCfg.inst.sendInterval; obj[F("intvl")] = mCfg.inst.sendInterval;
obj[F("en")] = (bool)mCfg.inst.enabled; // obj[F("en")] = (bool)mCfg.inst.enabled;
obj[F("rstMidNight")] = (bool)mCfg.inst.rstYieldMidNight; obj[F("rstMidNight")] = (bool)mCfg.inst.rstYieldMidNight;
obj[F("rstNotAvail")] = (bool)mCfg.inst.rstValsNotAvail; obj[F("rstNotAvail")] = (bool)mCfg.inst.rstValsNotAvail;
obj[F("rstComStop")] = (bool)mCfg.inst.rstValsCommStop; obj[F("rstComStop")] = (bool)mCfg.inst.rstValsCommStop;
obj[F("strtWthtTime")] = (bool)mCfg.inst.startWithoutTime; obj[F("strtWthtTime")] = (bool)mCfg.inst.startWithoutTime;
obj[F("rstMaxMidNight")] = (bool)mCfg.inst.rstMaxValsMidNight; obj[F("rstMaxMidNight")] = (bool)mCfg.inst.rstMaxValsMidNight;
obj[F("yldEff")] = mCfg.inst.yieldEffiency;
obj[F("rdGrid")] = (bool)mCfg.inst.readGrid; obj[F("rdGrid")] = (bool)mCfg.inst.readGrid;
} }
else { else {
getVal<uint16_t>(obj, F("intvl"), &mCfg.inst.sendInterval); getVal<uint16_t>(obj, F("intvl"), &mCfg.inst.sendInterval);
getVal<bool>(obj, F("en"), &mCfg.inst.enabled); // getVal<bool>(obj, F("en"), &mCfg.inst.enabled);
getVal<bool>(obj, F("rstMidNight"), &mCfg.inst.rstYieldMidNight); getVal<bool>(obj, F("rstMidNight"), &mCfg.inst.rstYieldMidNight);
getVal<bool>(obj, F("rstNotAvail"), &mCfg.inst.rstValsNotAvail); getVal<bool>(obj, F("rstNotAvail"), &mCfg.inst.rstValsNotAvail);
getVal<bool>(obj, F("rstComStop"), &mCfg.inst.rstValsCommStop); getVal<bool>(obj, F("rstComStop"), &mCfg.inst.rstValsCommStop);
getVal<bool>(obj, F("strtWthtTime"), &mCfg.inst.startWithoutTime); getVal<bool>(obj, F("strtWthtTime"), &mCfg.inst.startWithoutTime);
getVal<bool>(obj, F("rstMaxMidNight"), &mCfg.inst.rstMaxValsMidNight); getVal<bool>(obj, F("rstMaxMidNight"), &mCfg.inst.rstMaxValsMidNight);
getVal<float>(obj, F("yldEff"), &mCfg.inst.yieldEffiency);
getVal<bool>(obj, F("rdGrid"), &mCfg.inst.readGrid); getVal<bool>(obj, F("rdGrid"), &mCfg.inst.readGrid);
if(mCfg.inst.yieldEffiency < 0.5)
mCfg.inst.yieldEffiency = 1.0f;
else if(mCfg.inst.yieldEffiency > 1.0f)
mCfg.inst.yieldEffiency = 1.0f;
} }
JsonArray ivArr; JsonArray ivArr;

4
src/defines.h

@ -13,7 +13,7 @@
//------------------------------------- //-------------------------------------
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 8 #define VERSION_MINOR 8
#define VERSION_PATCH 85 #define VERSION_PATCH 89
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {
@ -112,7 +112,7 @@ enum {
typedef struct { typedef struct {
uint32_t rxFail; uint32_t rxFail;
uint32_t rxFailNoAnser; uint32_t rxFailNoAnswer;
uint32_t rxSuccess; uint32_t rxSuccess;
uint32_t frmCnt; uint32_t frmCnt;
uint32_t txCnt; uint32_t txCnt;

4
src/hm/Communication.h

@ -117,7 +117,7 @@ class Communication : public CommQueue<> {
//q->iv->radioStatistics.txCnt++; //q->iv->radioStatistics.txCnt++;
q->iv->radio->mRadioWaitTime.startTimeMonitor(mTimeout); q->iv->radio->mRadioWaitTime.startTimeMonitor(mTimeout);
if(!mIsRetransmit && (q->cmd == AlarmData) || (q->cmd == GridOnProFilePara)) if((!mIsRetransmit && (q->cmd == AlarmData)) || (q->cmd == GridOnProFilePara))
incrAttempt((q->cmd == AlarmData)? MORE_ATTEMPS_ALARMDATA : MORE_ATTEMPS_GRIDONPROFILEPARA); incrAttempt((q->cmd == AlarmData)? MORE_ATTEMPS_ALARMDATA : MORE_ATTEMPS_GRIDONPROFILEPARA);
mIsRetransmit = false; mIsRetransmit = false;
@ -622,7 +622,7 @@ class Communication : public CommQueue<> {
else if(q->iv->mGotFragment || mCompleteRetry) else if(q->iv->mGotFragment || mCompleteRetry)
q->iv->radioStatistics.rxFail++; // got no complete payload q->iv->radioStatistics.rxFail++; // got no complete payload
else else
q->iv->radioStatistics.rxFailNoAnser++; // got nothing q->iv->radioStatistics.rxFailNoAnswer++; // got nothing
mWaitTime.startTimeMonitor(1); // maybe remove, side effects unknown mWaitTime.startTimeMonitor(1); // maybe remove, side effects unknown
bool keep = false; bool keep = false;

15
src/hm/Heuristic.h

@ -153,7 +153,7 @@ class Heuristic {
DBGPRINT(F(", f: ")); DBGPRINT(F(", f: "));
DBGPRINT(String(iv->radioStatistics.rxFail)); DBGPRINT(String(iv->radioStatistics.rxFail));
DBGPRINT(F(", n: ")); DBGPRINT(F(", n: "));
DBGPRINT(String(iv->radioStatistics.rxFailNoAnser)); DBGPRINT(String(iv->radioStatistics.rxFailNoAnswer));
DBGPRINT(F(" | p: ")); // better debugging for helpers... DBGPRINT(F(" | p: ")); // better debugging for helpers...
if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen))
DBGPRINTLN(String(iv->config->powerLevel-10)); DBGPRINTLN(String(iv->config->powerLevel-10));
@ -217,15 +217,12 @@ class Heuristic {
} }
inline uint8_t id2Ch(uint8_t id) { inline uint8_t id2Ch(uint8_t id) {
switch(id) { if (id < RF_MAX_CHANNEL_ID)
case 0: return 3; return mChList[id];
case 1: return 23; else
case 2: return 40; return 3; // standard
case 3: return 61;
case 4: return 75;
}
return 3; // standard
} }
uint8_t mChList[RF_MAX_CHANNEL_ID] = {03, 23, 40, 61, 75};
}; };

50
src/hm/hmInverter.h

@ -288,32 +288,30 @@ class Inverter {
uint8_t end = ptr + rec->assign[pos].num; uint8_t end = ptr + rec->assign[pos].num;
uint16_t div = rec->assign[pos].div; uint16_t div = rec->assign[pos].div;
if(NULL != rec) { if(CMD_CALC != div) {
if(CMD_CALC != div) { uint32_t val = 0;
uint32_t val = 0; do {
do { val <<= 8;
val <<= 8; val |= buf[ptr];
val |= buf[ptr]; } while(++ptr != end);
} while(++ptr != end);
if ((FLD_T == rec->assign[pos].fieldId) || (FLD_Q == rec->assign[pos].fieldId) || (FLD_PF == rec->assign[pos].fieldId)) {
if ((FLD_T == rec->assign[pos].fieldId) || (FLD_Q == rec->assign[pos].fieldId) || (FLD_PF == rec->assign[pos].fieldId)) { // temperature, Qvar, and power factor are a signed values
// temperature, Qvar, and power factor are a signed values rec->record[pos] = ((REC_TYP)((int16_t)val)) / (REC_TYP)(div);
rec->record[pos] = ((REC_TYP)((int16_t)val)) / (REC_TYP)(div); } else if (FLD_YT == rec->assign[pos].fieldId) {
} else if (FLD_YT == rec->assign[pos].fieldId) { rec->record[pos] = ((REC_TYP)(val) / (REC_TYP)(div)) + ((REC_TYP)config->yieldCor[rec->assign[pos].ch-1]);
rec->record[pos] = ((REC_TYP)(val) / (REC_TYP)(div) * generalConfig->yieldEffiency) + ((REC_TYP)config->yieldCor[rec->assign[pos].ch-1]); } else if (FLD_YD == rec->assign[pos].fieldId) {
} else if (FLD_YD == rec->assign[pos].fieldId) { float actYD = (REC_TYP)(val) / (REC_TYP)(div);
float actYD = (REC_TYP)(val) / (REC_TYP)(div) * generalConfig->yieldEffiency; uint8_t idx = rec->assign[pos].ch - 1;
uint8_t idx = rec->assign[pos].ch - 1; if (mLastYD[idx] > actYD)
if (mLastYD[idx] > actYD) mOffYD[idx] += mLastYD[idx];
mOffYD[idx] += mLastYD[idx]; mLastYD[idx] = actYD;
mLastYD[idx] = actYD; rec->record[pos] = mOffYD[idx] + actYD;
rec->record[pos] = mOffYD[idx] + actYD; } else {
} else { if ((REC_TYP)(div) > 1)
if ((REC_TYP)(div) > 1) rec->record[pos] = (REC_TYP)(val) / (REC_TYP)(div);
rec->record[pos] = (REC_TYP)(val) / (REC_TYP)(div); else
else rec->record[pos] = (REC_TYP)(val);
rec->record[pos] = (REC_TYP)(val);
}
} }
} }

13
src/hm/hmSystem.h

@ -16,8 +16,8 @@ class HmSystem {
HmSystem() {} HmSystem() {}
void setup(uint32_t *timestamp, cfgInst_t *config, IApp *app) { void setup(uint32_t *timestamp, cfgInst_t *config, IApp *app) {
mInverter[0].timestamp = timestamp; INVERTERTYPE::timestamp = timestamp;
mInverter[0].generalConfig = config; INVERTERTYPE::generalConfig = config;
//mInverter[0].app = app; //mInverter[0].app = app;
} }
@ -35,6 +35,8 @@ class HmSystem {
case 0x21: iv->type = INV_TYPE_1CH; case 0x21: iv->type = INV_TYPE_1CH;
break; break;
case 0x25: // HMS-400 - 1 channel but payload like 2ch
case 0x44: // HMS-1000 case 0x44: // HMS-1000
case 0x42: case 0x42:
case 0x41: iv->type = INV_TYPE_2CH; case 0x41: iv->type = INV_TYPE_2CH;
@ -51,15 +53,14 @@ class HmSystem {
} }
if(iv->config->serial.b[5] == 0x11) { if(iv->config->serial.b[5] == 0x11) {
if((iv->config->serial.b[4] & 0x0f) == 0x04) { if(((iv->config->serial.b[4] & 0x0f) == 0x04) || ((iv->config->serial.b[4] & 0x0f) == 0x05)) {
iv->ivGen = IV_HMS; iv->ivGen = IV_HMS;
iv->ivRadioType = INV_RADIO_TYPE_CMT; iv->ivRadioType = INV_RADIO_TYPE_CMT;
} else { } else {
iv->ivGen = IV_HM; iv->ivGen = IV_HM;
iv->ivRadioType = INV_RADIO_TYPE_NRF; iv->ivRadioType = INV_RADIO_TYPE_NRF;
} }
} } else if((iv->config->serial.b[4] & 0x03) == 0x02) { // MI 3rd Gen -> same as HM
else if((iv->config->serial.b[4] & 0x03) == 0x02) { // MI 3rd Gen -> same as HM
iv->ivGen = IV_HM; iv->ivGen = IV_HM;
iv->ivRadioType = INV_RADIO_TYPE_NRF; iv->ivRadioType = INV_RADIO_TYPE_NRF;
} else { // MI 2nd Gen } else { // MI 2nd Gen
@ -82,7 +83,7 @@ class HmSystem {
DPRINT(DBG_INFO, "added inverter "); DPRINT(DBG_INFO, "added inverter ");
if(iv->config->serial.b[5] == 0x11) { if(iv->config->serial.b[5] == 0x11) {
if((iv->config->serial.b[4] & 0x0f) == 0x04) if(((iv->config->serial.b[4] & 0x0f) == 0x04) || ((iv->config->serial.b[4] & 0x0f) == 0x05))
DBGPRINT("HMS"); DBGPRINT("HMS");
else else
DBGPRINT("HM"); DBGPRINT("HM");

2
src/publisher/pubMqtt.h

@ -423,7 +423,7 @@ class PubMqtt {
} }
DynamicJsonDocument doc2(512); DynamicJsonDocument doc2(512);
constexpr static char* unitTotal[] = {"W", "kWh", "Wh", "W"}; constexpr static const char* unitTotal[] = {"W", "kWh", "Wh", "W"};
doc2[F("name")] = String(name.data()); doc2[F("name")] = String(name.data());
doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + ((!total) ? String(iv->config->name) : "total" ) + String(topic.data()); doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + ((!total) ? String(iv->config->name) : "total" ) + String(topic.data());
doc2[F("unit_of_meas")] = ((!total) ? (iv->getUnit(mDiscovery.sub, rec)) : (unitTotal[mDiscovery.sub])); doc2[F("unit_of_meas")] = ((!total) ? (iv->getUnit(mDiscovery.sub, rec)) : (unitTotal[mDiscovery.sub]));

4
src/publisher/pubMqttIvData.h

@ -222,7 +222,7 @@ class PubMqttIvData {
mIv->radioStatistics.txCnt, mIv->radioStatistics.txCnt,
mIv->radioStatistics.rxSuccess, mIv->radioStatistics.rxSuccess,
mIv->radioStatistics.rxFail, mIv->radioStatistics.rxFail,
mIv->radioStatistics.rxFailNoAnser, mIv->radioStatistics.rxFailNoAnswer,
mIv->radioStatistics.retransmits, mIv->radioStatistics.retransmits,
mIv->radioStatistics.ivLoss, mIv->radioStatistics.ivLoss,
mIv->radioStatistics.ivSent, mIv->radioStatistics.ivSent,
@ -293,7 +293,7 @@ class PubMqttIvData {
bool mRTRDataHasBeenSent = false; bool mRTRDataHasBeenSent = false;
std::array<char, (32 + MAX_NAME_LENGTH + 1)> mSubTopic; std::array<char, (32 + MAX_NAME_LENGTH + 1)> mSubTopic;
std::array<char, 140> mVal; std::array<char, 160> mVal;
std::queue<sendListCmdIv> *mSendList = nullptr; std::queue<sendListCmdIv> *mSendList = nullptr;
}; };

4
src/utils/scheduler.h

@ -125,8 +125,8 @@ namespace ah {
mTicker[i].timeout = timeout; mTicker[i].timeout = timeout;
mTicker[i].reload = reload; mTicker[i].reload = reload;
mTicker[i].isTimestamp = isTimestamp; mTicker[i].isTimestamp = isTimestamp;
memset(mTicker[i].name, 0, 6); strncpy(mTicker[i].name, name, 5);
strncpy(mTicker[i].name, name, (strlen(name) < 6) ? strlen(name) : 5); mTicker[i].name[5]=0;
if(mMax == i) if(mMax == i)
mMax = i + 1; mMax = i + 1;
return i; return i;

55
src/web/RestApi.h

@ -141,7 +141,7 @@ class RestApi {
DPRINTLN(DBG_VERBOSE, "onApiPostBody"); DPRINTLN(DBG_VERBOSE, "onApiPostBody");
if(0 == index) { if(0 == index) {
if(NULL != mTmpBuf) if(nullptr != mTmpBuf)
delete[] mTmpBuf; delete[] mTmpBuf;
mTmpBuf = new uint8_t[total+1]; mTmpBuf = new uint8_t[total+1];
mTmpSize = total; mTmpSize = total;
@ -154,36 +154,40 @@ class RestApi {
DynamicJsonDocument json(1000); DynamicJsonDocument json(1000);
DeserializationError err = deserializeJson(json, reinterpret_cast<const char*>(mTmpBuf), mTmpSize);
JsonObject obj = json.as<JsonObject>();
AsyncJsonResponse* response = new AsyncJsonResponse(false, 200); AsyncJsonResponse* response = new AsyncJsonResponse(false, 200);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root[F("success")] = (err) ? false : true; DeserializationError err = deserializeJson(json, reinterpret_cast<const char*>(mTmpBuf), mTmpSize);
if(!err) { if(!json.is<JsonObject>())
String path = request->url().substring(5); root[F("error")] = F(DESER_FAILED);
if(path == "ctrl") else {
root[F("success")] = setCtrl(obj, root, request->client()->remoteIP().toString().c_str()); JsonObject obj = json.as<JsonObject>();
else if(path == "setup")
root[F("success")] = setSetup(obj, root, request->client()->remoteIP().toString().c_str()); root[F("success")] = (err) ? false : true;
else { if(!err) {
root[F("success")] = false; String path = request->url().substring(5);
root[F("error")] = F(PATH_NOT_FOUND) + path; if(path == "ctrl")
} root[F("success")] = setCtrl(obj, root, request->client()->remoteIP().toString().c_str());
} else { else if(path == "setup")
switch (err.code()) { root[F("success")] = setSetup(obj, root, request->client()->remoteIP().toString().c_str());
case DeserializationError::Ok: break; else {
case DeserializationError::IncompleteInput: root[F("error")] = F(INCOMPLETE_INPUT); break; root[F("success")] = false;
case DeserializationError::InvalidInput: root[F("error")] = F(INVALID_INPUT); break; root[F("error")] = F(PATH_NOT_FOUND) + path;
case DeserializationError::NoMemory: root[F("error")] = F(NOT_ENOUGH_MEM); break; }
default: root[F("error")] = F(DESER_FAILED); break; } else {
switch (err.code()) {
case DeserializationError::Ok: break;
case DeserializationError::IncompleteInput: root[F("error")] = F(INCOMPLETE_INPUT); break;
case DeserializationError::InvalidInput: root[F("error")] = F(INVALID_INPUT); break;
case DeserializationError::NoMemory: root[F("error")] = F(NOT_ENOUGH_MEM); break;
default: root[F("error")] = F(DESER_FAILED); break;
}
} }
} }
response->setLength(); response->setLength();
request->send(response); request->send(response);
delete[] mTmpBuf; delete[] mTmpBuf;
mTmpBuf = NULL; mTmpBuf = nullptr;
} }
void getNotFound(JsonObject obj, String url) { void getNotFound(JsonObject obj, String url) {
@ -422,7 +426,7 @@ class RestApi {
obj[F("name")] = String(iv->config->name); obj[F("name")] = String(iv->config->name);
obj[F("rx_success")] = iv->radioStatistics.rxSuccess; obj[F("rx_success")] = iv->radioStatistics.rxSuccess;
obj[F("rx_fail")] = iv->radioStatistics.rxFail; obj[F("rx_fail")] = iv->radioStatistics.rxFail;
obj[F("rx_fail_answer")] = iv->radioStatistics.rxFailNoAnser; obj[F("rx_fail_answer")] = iv->radioStatistics.rxFailNoAnswer;
obj[F("frame_cnt")] = iv->radioStatistics.frmCnt; obj[F("frame_cnt")] = iv->radioStatistics.frmCnt;
obj[F("tx_cnt")] = iv->radioStatistics.txCnt; obj[F("tx_cnt")] = iv->radioStatistics.txCnt;
obj[F("retransmits")] = iv->radioStatistics.retransmits; obj[F("retransmits")] = iv->radioStatistics.retransmits;
@ -480,7 +484,6 @@ class RestApi {
obj[F("strtWthtTm")] = (bool)mConfig->inst.startWithoutTime; obj[F("strtWthtTm")] = (bool)mConfig->inst.startWithoutTime;
obj[F("rdGrid")] = (bool)mConfig->inst.readGrid; obj[F("rdGrid")] = (bool)mConfig->inst.readGrid;
obj[F("rstMaxMid")] = (bool)mConfig->inst.rstMaxValsMidNight; obj[F("rstMaxMid")] = (bool)mConfig->inst.rstMaxValsMidNight;
obj[F("yldEff")] = mConfig->inst.yieldEffiency;
} }
void getInverter(JsonObject obj, uint8_t id) { void getInverter(JsonObject obj, uint8_t id) {
@ -998,7 +1001,7 @@ class RestApi {
uint32_t mTimezoneOffset = 0; uint32_t mTimezoneOffset = 0;
uint32_t mHeapFree = 0, mHeapFreeBlk = 0; uint32_t mHeapFree = 0, mHeapFreeBlk = 0;
uint8_t mHeapFrag = 0; uint8_t mHeapFrag = 0;
uint8_t *mTmpBuf = NULL; uint8_t *mTmpBuf = nullptr;
uint32_t mTmpSize = 0; uint32_t mTmpSize = 0;
}; };

1
src/web/html/history.html

@ -80,6 +80,7 @@
function parsePowerHistory(obj){ function parsePowerHistory(obj){
if (null != obj) { if (null != obj) {
parseNav(obj.generic); parseNav(obj.generic);
parseESP(obj.generic);
parseHistory(obj,"pwr", pwrExeOnce) parseHistory(obj,"pwr", pwrExeOnce)
document.getElementById("pwrLast").innerHTML = mLastValue document.getElementById("pwrLast").innerHTML = mLastValue
document.getElementById("pwrMaxDay").innerHTML = obj.maxDay document.getElementById("pwrMaxDay").innerHTML = obj.maxDay

6
src/web/html/includes/nav.html

@ -10,15 +10,15 @@
<a id="nav11" class="acitve" href="/history?v={#VERSION}">{#NAV_HISTORY}</a> <a id="nav11" class="acitve" href="/history?v={#VERSION}">{#NAV_HISTORY}</a>
<a id="nav4" class="hide" href="/serial?v={#VERSION}">{#NAV_WEBSERIAL}</a> <a id="nav4" class="hide" href="/serial?v={#VERSION}">{#NAV_WEBSERIAL}</a>
<a id="nav5" class="hide" href="/setup?v={#VERSION}">{#NAV_SETTINGS}</a> <a id="nav5" class="hide" href="/setup?v={#VERSION}">{#NAV_SETTINGS}</a>
<span class="seperator"></span> <span class="separator"></span>
<a id="nav6" class="hide" href="/update?v={#VERSION}">Update</a> <a id="nav6" class="hide" href="/update?v={#VERSION}">Update</a>
<a id="nav7" class="hide" href="/system?v={#VERSION}">System</a> <a id="nav7" class="hide" href="/system?v={#VERSION}">System</a>
<span class="seperator"></span> <span class="separator"></span>
<a id="nav8" href="/api" target="_blank">REST API</a> <a id="nav8" href="/api" target="_blank">REST API</a>
<a id="nav9" href="https://ahoydtu.de" target="_blank">{#NAV_DOCUMENTATION}</a> <a id="nav9" href="https://ahoydtu.de" target="_blank">{#NAV_DOCUMENTATION}</a>
<a id="nav10" href="/about?v={#VERSION}">{#NAV_ABOUT}</a> <a id="nav10" href="/about?v={#VERSION}">{#NAV_ABOUT}</a>
<a id="nav12" href="#" class="hide" target="_blank">Custom Link</a> <a id="nav12" href="#" class="hide" target="_blank">Custom Link</a>
<span class="seperator"></span> <span class="separator"></span>
<a id="nav0" class="hide" href="/login">Login</a> <a id="nav0" class="hide" href="/login">Login</a>
<a id="nav1" class="hide" href="/logout">Logout</a> <a id="nav1" class="hide" href="/logout">Logout</a>
</div> </div>

4
src/web/html/index.html

@ -70,9 +70,9 @@
var min = parseInt(up / 60) % 60; var min = parseInt(up / 60) % 60;
var sec = up % 60; var sec = up % 60;
var e = document.getElementById("uptime"); var e = document.getElementById("uptime");
e.innerHTML = days + " Day"; e.innerHTML = days + " {#DAY}";
if(1 != days) if(1 != days)
e.innerHTML += "s"; e.innerHTML += "{#S}";
e.innerHTML += ", " + ("0"+hrs).substr(-2) + ":" e.innerHTML += ", " + ("0"+hrs).substr(-2) + ":"
+ ("0"+min).substr(-2) + ":" + ("0"+min).substr(-2) + ":"
+ ("0"+sec).substr(-2); + ("0"+sec).substr(-2);

6
src/web/html/serial.html

@ -65,7 +65,7 @@
}); });
document.getElementById("scroll").addEventListener("click", function() { document.getElementById("scroll").addEventListener("click", function() {
mAutoScroll = !mAutoScroll; mAutoScroll = !mAutoScroll;
this.value = (mAutoScroll) ? "autoscroll" : "manual scroll"; this.value = (mAutoScroll) ? "{#BTN_AUTOSCROLL}" : "{#BTN_MANUALSCROLL}";
}); });
document.getElementById("copy").addEventListener("click", function() { document.getElementById("copy").addEventListener("click", function() {
con.value = version + " - " + build + "\n---------------\n" + con.value; con.value = version + " - " + build + "\n---------------\n" + con.value;
@ -80,10 +80,10 @@
try { try {
return document.execCommand("copy"); // Security exception may be thrown by some browsers. return document.execCommand("copy"); // Security exception may be thrown by some browsers.
} catch (ex) { } catch (ex) {
alert("Copy to clipboard failed" + ex); alert("{#CLIPBOARD_FAILED} " + ex);
} finally { } finally {
document.body.removeChild(ta); document.body.removeChild(ta);
alert("Copied to clipboard"); alert("{#COPIED_TO_CLIPBOARD}");
} }
} }
}); });

9
src/web/html/setup.html

@ -157,10 +157,6 @@
<div class="col-8">{#INV_READ_GRID_PROFILE}</div> <div class="col-8">{#INV_READ_GRID_PROFILE}</div>
<div class="col-4"><input type="checkbox" name="rdGrid"/></div> <div class="col-4"><input type="checkbox" name="rdGrid"/></div>
</div> </div>
<div class="row mb-3">
<div class="col-8">{#INV_YIELD_EFF}</div>
<div class="col-4"><input type="number" name="yldEff" step="any"/></div>
</div>
</fieldset> </fieldset>
</div> </div>
@ -652,7 +648,7 @@
} }
function ivGlob(obj) { function ivGlob(obj) {
for(var i of [["invInterval", "interval"], ["yldEff", "yldEff"]]) for(var i of [["invInterval", "interval"]])
document.getElementsByName(i[0])[0].value = obj[i[1]]; document.getElementsByName(i[0])[0].value = obj[i[1]];
for(var i of ["Mid", "ComStop", "NotAvail", "MaxMid"]) for(var i of ["Mid", "ComStop", "NotAvail", "MaxMid"])
document.getElementsByName("invRst"+i)[0].checked = obj["rst" + i]; document.getElementsByName("invRst"+i)[0].checked = obj["rst" + i];
@ -833,7 +829,8 @@
case 0x1000: nrf = true; break; case 0x1000: nrf = true; break;
case 0x1100: case 0x1100:
switch(sn & 0x000f) { switch(sn & 0x000f) {
case 0x0004: nrf = false; break; case 0x0004:
case 0x0005: nrf = false; break;
default: nrf = true; break; default: nrf = true; break;
} }
break; break;

2
src/web/html/style.css

@ -139,7 +139,7 @@ svg.icon {
background-color: var(--nav-active); background-color: var(--nav-active);
} }
span.seperator { span.separator {
width: 100%; width: 100%;
height: 1px; height: 1px;
margin: 5px 0 5px; margin: 5px 0 5px;

37
src/web/lang.json

@ -295,8 +295,8 @@
}, },
{ {
"token": "INV_RESET_MIDNIGHT", "token": "INV_RESET_MIDNIGHT",
"en": "Reset values and YieldDay at midnight. ('Pause communication during night' need to be set)", "en": "Reset values and YieldDay at midnight",
"de": "Werte und Gesamtertrag um Mitternacht zur&uuml;cksetzen ('Kommunikation w&auml;hrend der Nacht pausieren' muss gesetzt sein)" "de": "Werte und Gesamtertrag um Mitternacht zur&uuml;cksetzen"
}, },
{ {
"token": "INV_PAUSE_SUNSET", "token": "INV_PAUSE_SUNSET",
@ -323,11 +323,6 @@
"en": "Read Grid Profile", "en": "Read Grid Profile",
"de": "Grid-Profil auslesen" "de": "Grid-Profil auslesen"
}, },
{
"token": "INV_YIELD_EFF",
"en": "Yield Efficiency (default 1.0)",
"de": "Ertragseffizienz (Standard 1.0)"
},
{ {
"token": "NTP_INTERVAL", "token": "NTP_INTERVAL",
"en": "NTP Interval (in minutes, min. 5 minutes)", "en": "NTP Interval (in minutes, min. 5 minutes)",
@ -883,6 +878,11 @@
"en": "autoscroll", "en": "autoscroll",
"de": "automatisch scrollen" "de": "automatisch scrollen"
}, },
{
"token": "BTN_MANUALSCROLL",
"en": "manual scroll",
"de": "manuell scrollen"
},
{ {
"token": "BTN_COPY", "token": "BTN_COPY",
"en": "copy", "en": "copy",
@ -897,12 +897,21 @@
"token": "UPTIME", "token": "UPTIME",
"en": "uptime", "en": "uptime",
"de": "Laufzeit" "de": "Laufzeit"
} },
,
{ {
"token": "DAYS", "token": "DAYS",
"en": "days", "en": "days",
"de": "Tage" "de": "Tage"
},
{
"token": "COPIED_TO_CLIPBOARD",
"en": "Copied to clipboard",
"de": "in die Zwischenablage kopiert"
},
{
"token": "CLIPBOARD_FAILED",
"en": "Copy failed",
"de": "kopieren fehlgeschlagen"
} }
] ]
}, },
@ -979,6 +988,16 @@
"en": "Error", "en": "Error",
"de": "Fehler" "de": "Fehler"
}, },
{
"token": "DAY",
"en": "day",
"de": "Tag"
},
{
"token": "S",
"en": "s",
"de": "e"
},
{ {
"token": "NTP_UNREACH", "token": "NTP_UNREACH",
"en": "NTP timeserver unreachable", "en": "NTP timeserver unreachable",

3
src/web/web.h

@ -486,7 +486,6 @@ class Web {
mConfig->inst.startWithoutTime = (request->arg("strtWthtTm") == "on"); mConfig->inst.startWithoutTime = (request->arg("strtWthtTm") == "on");
mConfig->inst.readGrid = (request->arg("rdGrid") == "on"); mConfig->inst.readGrid = (request->arg("rdGrid") == "on");
mConfig->inst.rstMaxValsMidNight = (request->arg("invRstMaxMid") == "on"); mConfig->inst.rstMaxValsMidNight = (request->arg("invRstMaxMid") == "on");
mConfig->inst.yieldEffiency = (request->arg("yldEff")).toFloat();
// pinout // pinout
@ -701,7 +700,7 @@ class Web {
{ "max_power", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->getMaxPower();} }, { "max_power", "gauge", metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->getMaxPower();} },
{ "radio_rx_success", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.rxSuccess;} }, { "radio_rx_success", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.rxSuccess;} },
{ "radio_rx_fail", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.rxFail;} }, { "radio_rx_fail", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.rxFail;} },
{ "radio_rx_fail_answer", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.rxFailNoAnser;} }, { "radio_rx_fail_answer", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.rxFailNoAnswer;} },
{ "radio_frame_cnt", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.frmCnt;} }, { "radio_frame_cnt", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.frmCnt;} },
{ "radio_tx_cnt", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.txCnt;} }, { "radio_tx_cnt", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.txCnt;} },
{ "radio_retransmits", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.retransmits;} }, { "radio_retransmits", "counter" ,metricConstInverterFormat, [](Inverter<> *iv)-> uint64_t {return iv->radioStatistics.retransmits;} },

Loading…
Cancel
Save