diff --git a/scripts/convertHtml.py b/scripts/convertHtml.py index 70d5f6e2..6eaa92a3 100644 --- a/scripts/convertHtml.py +++ b/scripts/convertHtml.py @@ -159,7 +159,7 @@ if os.path.exists(wd): # grab all files with following extensions os.chdir('./web/html') -types = ('*.html', '*.css', '*.js', '*.ico') # the tuple of file types +types = ('*.html', '*.css', '*.js', '*.ico', '*.json') # the tuple of file types files_grabbed = [] for files in types: files_grabbed.extend(glob.glob(files)) diff --git a/src/CHANGES.md b/src/CHANGES.md index e9563510..99b7dc64 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,9 +1,12 @@ # Development Changes -## 0.8.29 - 2023-12-23 +## 0.8.29 - 2023-12-27 * fix MqTT generic topic `comm_disabled` #1265 #1286 * potential fix of #1285 (reset yield day) * fix fraction of yield correction #1280 +* fix crash if `getLossRate` was read from inverter #1288 #1290 +* reduce reload time for opendtufusion ethernet variant to 5 seconds +* added basic grid parser ## 0.8.28 - 2023-12-23 * fix bug heuristic diff --git a/src/hm/Communication.h b/src/hm/Communication.h index 818b396d..23d20004 100644 --- a/src/hm/Communication.h +++ b/src/hm/Communication.h @@ -117,28 +117,6 @@ class Communication : public CommQueue<> { break; case States::WAIT: - /*if(millis() > mWaitTimeout_min) { - if(mIsRetransmit) { // we already have been through... - mWaitTimeout = mWaitTimeout_min; - } else if(q->iv->mGotFragment) { // nothing received yet? - if(q->iv->mGotLastMsg) { - //mState = States::CHECK_FRAMES; - mWaitTimeout = mWaitTimeout_min; - } - } else if(mFirstTry) { - if(*mSerialDebug) { - DPRINT_IVID(DBG_INFO, q->iv->id); - DBGPRINT(String(millis() - mWaitTimeout_min + mlastTO_min)); - DBGPRINTLN(F("ms - second try")); - } - mFirstTry = false; - mlastTO_min = timeout_min; - q->iv->radioStatistics.retransmits++; // got nothing - mState = States::START; - break; - } - - }*/ if(millis() < mWaitTimeout) return; mState = States::CHECK_FRAMES; @@ -187,7 +165,7 @@ class Communication : public CommQueue<> { if(parseMiFrame(p, q)) q->iv->curFrmCnt++; } - } //else // serial does not match + } //else -> serial does not match q->iv->radio->mBufCtrl.pop(); yield(); @@ -221,7 +199,6 @@ class Communication : public CommQueue<> { else closeRequest(q, true); } - } } @@ -271,7 +248,7 @@ class Communication : public CommQueue<> { compilePayload(q); - if((NULL != mCbPayload) && (GridOnProFilePara != q->cmd)) + if((NULL != mCbPayload) && (GridOnProFilePara != q->cmd) && (GetLossRate != q->cmd)) (mCbPayload)(q->cmd, q->iv); closeRequest(q, true); @@ -306,7 +283,7 @@ class Communication : public CommQueue<> { else ah::dumpBuf(p->packet, p->len); } else { - DBGPRINT(F("| 0x")); + DBGPRINT(F("| ")); DHEX(p->packet[0]); DBGPRINT(F(" ")); DBGHEXLN(p->packet[9]); diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 6db39d7f..1c4e26e9 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -98,7 +98,7 @@ class HmRadio : public Radio { if(mNrf24->isChipConnected()) { DPRINTLN(DBG_INFO, F("Radio Config:")); mNrf24->printPrettyDetails(); - DPRINT(DBG_INFO, F("DTU_SN: 0x")); + DPRINT(DBG_INFO, F("DTU_SN: ")); DBGPRINTLN(String(mDtuSn, HEX)); } else DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring")); @@ -152,7 +152,7 @@ class HmRadio : public Radio { void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) { DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("sendControlPacket cmd: 0x")); + DBGPRINT(F("sendControlPacket cmd: ")); DBGHEXLN(cmd); initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; @@ -307,9 +307,8 @@ class HmRadio : public Radio { else ah::dumpBuf(mTxBuf, len); } else { - DBGPRINT(F("0x")); DHEX(mTxBuf[0]); - DBGPRINT(F(" 0x")); + DBGPRINT(F(" ")); DHEX(mTxBuf[10]); DBGPRINT(F(" ")); DBGHEXLN(mTxBuf[9]); diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index 80a2ecd9..2b147107 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -42,7 +42,7 @@ class CmtRadio : public Radio { } void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) { - DPRINT(DBG_INFO, F("sendControlPacket cmd: 0x")); + DPRINT(DBG_INFO, F("sendControlPacket cmd: ")); DBGHEXLN(cmd); initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; @@ -96,9 +96,8 @@ class CmtRadio : public Radio { else ah::dumpBuf(mTxBuf, len); } else { - DBGPRINT(F("0x")); DHEX(mTxBuf[0]); - DBGPRINT(F(" 0x")); + DBGPRINT(F(" ")); DHEX(mTxBuf[10]); DBGHEXLN(mTxBuf[9]); } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 990f3e92..f2c193cd 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -323,9 +323,14 @@ class RestApi { void getHtmlSave(AsyncWebServerRequest *request, JsonObject obj) { getGeneric(request, obj.createNestedObject(F("generic"))); - obj["pending"] = (bool)mApp->getSavePending(); - obj["success"] = (bool)mApp->getLastSaveSucceed(); - obj["reboot"] = (bool)mApp->getShouldReboot(); + obj[F("pending")] = (bool)mApp->getSavePending(); + obj[F("success")] = (bool)mApp->getLastSaveSucceed(); + obj[F("reboot")] = (bool)mApp->getShouldReboot(); + #if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3) + obj[F("reload")] = 5; + #else + obj[F("reload")] = 20; + #endif } void getReboot(AsyncWebServerRequest *request, JsonObject obj) { diff --git a/src/web/html/api.js b/src/web/html/api.js index e76aab2c..b059cc69 100644 --- a/src/web/html/api.js +++ b/src/web/html/api.js @@ -175,6 +175,14 @@ function getAjax(url, ptr, method="GET", json=null) { } } +const getJSON = async url => { + const re = await fetch(url); + if(!re.ok) + throw new Error(re.statusText); + const data = re.json(); + return data; +} + /** * CREATE DOM FUNCTIONS */ diff --git a/src/web/html/grid_info.json b/src/web/html/grid_info.json new file mode 100644 index 00000000..adb68393 --- /dev/null +++ b/src/web/html/grid_info.json @@ -0,0 +1,764 @@ +{ + "type": [ + {"0x0300": "DE_VDE4105_2018"}, + {"0x0a00": "DE NF_EN_50549-1:2019"}, + {"0x0c00": "AT_TOR_Erzeuger_default"}, + {"0x0d04": "France NF_EN_50549-1:2019"}, + {"0x1200": "2.0.4 (EU_EN50438)"}, + {"0x3700": "2.0.0 (CH_NA EEA-NE7–CH2020)"} + ], + "grp_codes": [ + {"0x00": "Voltage H/LVRT"}, + {"0x10": "Frequency H/LFRT"}, + {"0x20": "Islanding Detection"}, + {"0x30": "Reconnection"}, + {"0x40": "Ramp Rates"}, + {"0x50": "Frequency Watt"}, + {"0x60": "Volt Watt"}, + {"0x70": "Active Power Control"}, + {"0x80": "Volt Var"}, + {"0x90": "Specified Power Factor"}, + {"0xa0": "Reactive Power Control"}, + {"0xb0": "Watt Power Factor"} + ], + "group": [ + { + "0x0003": [ + { + "name": "Nominal Voltage", + "div": 10, + "def": 230, + "unit": "V" + }, + { + "name": "Low Voltage 1", + "div": 10, + "min": 180, + "max": 207, + "def": 184, + "unit": "V" + }, + { + "name": "LV1 Maximum Trip Time", + "div": 10, + "def": 1.5, + "unit": "s" + }, + { + "name": "High Voltage 1", + "div": 10, + "min": 250, + "max": 270, + "def": 253, + "unit": "V" + }, + { + "name": "HV1 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 100, + "def": 0.1, + "unit": "s" + }, + { + "name": "Low Voltage 2", + "div": 10, + "min": 80, + "max": 161, + "def": 104, + "unit": "V" + }, + { + "name": "LV2 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 5, + "def": 0.3, + "unit": "s" + }, + { + "name": "High Voltage 2", + "div": 10, + "min": 230, + "max": 299, + "def": 276, + "unit": "V" + }, + { + "name": "HV2 Maximum Trip Time", + "div": 100, + "min": 0, + "max": 5, + "def": 0.05, + "unit": "s" + } + ] + }, + { + "0x000a": [ + { + "name": "Nominal Voltage", + "div": 10, + "def": 230, + "unit": "V" + }, + { + "name": "Low Voltage 1", + "div": 10, + "min": 160, + "max": 195.5, + "def": 184, + "unit": "V" + }, + { + "name": "LV1 Maximum Trip Time", + "div": 10, + "def": 3, + "unit": "s" + }, + { + "name": "High Voltage 1", + "div": 10, + "min": 270, + "max": 287.5, + "def": 287.5, + "unit": "V" + }, + { + "name": "HV1 Maximum Trip Time", + "div": 10, + "def": 0.1, + "unit": "s" + }, + { + "name": "Low Voltage 2", + "div": 10, + "max": 150, + "min": 100, + "def": 103.5, + "unit": "V" + }, + { + "name": "LV2 Maximum Trip Time", + "div": 100, + "def": 0.3, + "unit": "s" + }, + { + "name": "10 mins Average High Voltage", + "div": 10, + "min": 250, + "max": 270, + "def": 253, + "unit": "V" + } + ] + }, + { + "0x000c": [ + { + "name": "Nominal Voltage", + "div": 10, + "unit": "V" + }, + { + "name": "Low Voltage 1", + "div": 10, + "min": 180, + "max": 207, + "def": 184, + "unit": "V" + }, + { + "name": "LV1 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 5, + "def": 1.5, + "unit": "s" + }, + { + "name": "High Voltage 1", + "div": 10, + "min": 250, + "max": 270, + "def": 253, + "unit": "V" + }, + { + "name": "HV1 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 100, + "def": 3, + "unit": "s" + }, + { + "name": "Low Voltage 2", + "div": 10, + "min": 80, + "max": 161, + "def": 161, + "unit": "V" + }, + { + "name": "LV2 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 5, + "def": 0.2, + "unit": "s" + }, + { + "name": "High Voltage 2", + "div": 10, + "min": 230, + "max": 299, + "def": 264.5, + "unit": "V" + }, + { + "name": "HV2 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 5, + "def": 0.2, + "unit": "s" + }, + { + "name": "High Voltage 3", + "div": 10, + "def": 276, + "unit": "V" + }, + { + "name": "HV3 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 10, + "def": 0.1, + "unit": "s" + }, + { + "name": "10 mins Average High Voltage", + "div": 10, + "def": 253, + "unit": "V" + } + ] + }, + { + "0x1000": [ + { + "name": "Nominal Frequency", + "div": 100, + "def": 50, + "unit": "Hz" + }, + { + "name": "Low Frequency 1", + "div": 100, + "min": 47.5, + "max": 49.9, + "def": 47.5, + "unit": "Hz" + }, + { + "name": "LF1 Maximum Trip Time", + "div": 10, + "def": 0.1, + "unit": "s" + }, + { + "name": "High Frequency 1", + "div": 100, + "min": 50.1, + "max": 51.5, + "def": 51.5, + "unit": "Hz" + }, + { + "name": "HF1 Maximum Trip Time", + "div": 10, + "def": 0.1, + "unit": "s" + } + ] + }, + { + "0x1003": [ + { + "name": "Nominal Frequency", + "div": 100, + "def": 50, + "unit": "Hz" + }, + { + "name": "Low Frequency 1", + "div": 100, + "min": 45, + "max": 49.9, + "def": 48, + "unit": "Hz" + }, + { + "name": "LF1 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 20, + "def": 2, + "unit": "s" + }, + { + "name": "High Frequency 1", + "div": 100, + "min": 50, + "max": 53, + "def": 51, + "unit": "Hz" + }, + { + "name": "HF1 Maximum Trip time", + "div": 10, + "min": 0.1, + "max": 20, + "def": 2, + "unit": "s" + }, + { + "name": "Low Frequency 2", + "div": 100, + "min": 45, + "max": 50, + "def": 47.5, + "unit": "Hz" + }, + { + "name": "LF2 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 5, + "def": 0.5, + "unit": "s" + }, + { + "name": "High Frequency 2", + "div": 100, + "min": 50, + "max": 52, + "def": 52, + "unit": "Hz" + }, + { + "name": "HF2 Maximum Trip time", + "div": 10, + "min": 0.1, + "max": 5, + "def": 0.5, + "unit": "s" + } + ] + }, + { + "0x2000": [ + { + "name": "Island Detection Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + } + ] + }, + { + "0x3003": [ + { + "name": "Reconnect Time", + "div": 10, + "min": 10, + "max": 300, + "def": 60, + "unit": "s" + }, + { + "name": "Reconnect High Voltage", + "div": 10, + "min": 240, + "max": 276, + "def": 253, + "unit": "V" + }, + { + "name": "Reconnect Low Voltage", + "div": 10, + "min": 195.5, + "max": 210, + "def": 195.5, + "unit": "V" + }, + { + "name": "Reconnect High Frequency", + "div": 100, + "max": 50.9, + "min": 50.1, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "Reconnect Low Frequency", + "div": 100, + "min": 47.5, + "max": 49.9, + "def": 49.5, + "unit": "Hz" + } + ] + }, + { + "0x4000": [ + { + "name": "Normal Ramp up Rate", + "div": 100, + "min": 0.1, + "max": 100, + "def": 20, + "unit": "Rated%/s" + }, + { + "name": "Soft Start Ramp up Rate ", + "div": 100, + "min": 0.1, + "max": 10, + "def": 0.16, + "unit": "Rated%/s" + } + ] + }, + { + "0x5001": [ + { + "name": "FW Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Start of Frequency Watt Droop", + "div": 100, + "min": 50.2, + "max": 53, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "FW Droop Slope", + "div": 10, + "min": 16.7, + "max": 100, + "def": 40, + "unit": "Pn%/Hz" + }, + { + "name": "Recovery Ramp Rate", + "div": 100, + "min": 0.1, + "max": 100, + "def": 0.16, + "unit": "Pn%/s" + + }, + { + "name": "FW Setting Time", + "div": 10, + "min": 0, + "max": 2, + "def": 0, + "unit": "s" + } + ] + }, + { + "0x5008": [ + { + "name": "FW Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Start of Frequency Watt Droop", + "div": 100, + "min": 50.2, + "max": 52, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "FW Droop Slope", + "div": 10, + "min": 16.7, + "max": 100, + "def": 40, + "unit": "Pn%/Hz" + }, + { + "name": "Recovery Ramp Rate", + "div": 100, + "min": 0.1, + "max": 50, + "def": 0.16, + "unit": "Pn%/s" + }, + { + "name": "Recovery High Frequency", + "div": 10, + "min": 50.1, + "max": 52, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "Recovery Low Frequency", + "div": 100, + "min": 49, + "max": 49.9, + "def": 49.8, + "unit": "Hz" + } + ] + }, + { + "0x6000": [ + { + "name": "VW Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Start of Voltage Watt Droop", + "div": 10, + "def": 253, + "unit": "V" + }, + { + "name": "End of Voltage Watt Droop", + "div": 10, + "min": 258, + "max": 270, + "def": 265, + "unit": "V" + }, + { + "name": "VW Droop Slope", + "div": 100, + "min": 5, + "max": 18, + "def": 5.33, + "unit": "Pn%/V" + } + ] + }, + { + "0x7002": [ + { + "name": "APC Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Power Ramp Rate", + "div": 100, + "min": 0.33, + "max": 100, + "def": 100, + "unit": "Pn%/s" + } + ] + }, + { + "0x8000": [ + { + "name": "VV Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Voltage Set Point V1", + "div": 10, + "min": 184, + "max": 230, + "def": 213.9, + "unit": "V" + }, + { + "name": "Reactive Set Point Q1", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + }, + { + "name": "Voltage Set Point V2", + "div": 10, + "min": 210, + "max": 240, + "def": 223.1, + "unit": "V" + }, + { + "name": "Voltage Set Point V3", + "div": 10, + "min": 220, + "max": 240, + "def": 236.9, + "unit": "V" + }, + { + "name": "Voltage Set Point V4", + "div": 10, + "min": 230, + "max": 253, + "def": 246.1, + "unit": "V" + }, + { + "name": "Reactive Set Point Q4", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + } + ] + }, + { + "0x8001": [ + { + "name": "VV Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Voltage Set Point V1", + "div": 10, + "def": 213.9, + "unit": "V" + }, + { + "name": "Reactive Set Point Q1", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + }, + { + "name": "Voltage Set Point V2", + "div": 10, + "def": 223.1, + "unit": "V" + }, + { + "name": "Voltage Set Point V3", + "div": 10, + "def": 236.9, + "unit": "V" + }, + { + "name": "Voltage Set Point V4", + "div": 10, + "def": 246.1, + "unit": "V" + }, + { + "name": "Reactive Set Point Q4", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + }, + { + "name": "VV Setting Time", + "div": 10, + "min": 0, + "max": 60, + "def": 10, + "unit": "s" + } + ] + }, + { + "0x9000": [ + { + "name": "Specified Power Factor Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Power Factor", + "div": 100, + "min": 0.9, + "max": 1, + "def": 0.95 + } + ] + }, + { + "0xa002": [ + { + "name": "RPC Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Reactive Power", + "div": 100, + "min": 0, + "max": 50, + "def": 0, + "unit": "%Sn" + } + ] + }, + { + "0xb000": [ + { + "name": "WPF Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Start of Power of WPF", + "div": 10, + "def": 50, + "unit": "%Pn" + }, + { + "name": "Power Factor ar Rated Power", + "div": 100, + "min": 0.8, + "max": 1, + "def": 0.95 + } + ] + } + ] +} diff --git a/src/web/html/save.html b/src/web/html/save.html index ce50c6ca..9b5b4864 100644 --- a/src/web/html/save.html +++ b/src/web/html/save.html @@ -34,8 +34,8 @@ html = "Settings successfully saved. Automatic page reload in 3 seconds."; meta.content = 3; } else { - html = "Settings successfully saved. Rebooting. Automatic redirect in 20 seconds."; - meta.content = 20 + "; URL=/"; + html = "Settings successfully saved. Rebooting. Automatic redirect in " + obj.reload + " seconds."; + meta.content = obj.reload + "; URL=/"; } document.getElementsByTagName('head')[0].appendChild(meta); } else { diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index faf76432..b561d5cc 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -295,13 +295,76 @@ getAjax("/api/inverter/grid/" + obj.id, showGridProfile); }}, null)) ]) - ]); - modal("Info for inverter " + obj.name, ml("div", {}, html)); + ]) + modal("Info for inverter " + obj.name, ml("div", {}, html)) + } + + function getGridValue(g) { + var val = (parseInt(g.grid.substring(g.offs*3, g.offs*3+2), 16) * 256) + + parseInt(g.grid.substring(g.offs*3+3, g.offs*3+5), 16) + g.offs += 2 + return val + } + + function getGridIdentifier(g) { + return "0x" + getGridValue(g).toString(16).padStart(4, '0') + } + + function getGridType(t, id) { + for(e of t) { + if(undefined !== e[id]) + return e[id] + } + return null + } + + function parseGridGroup(g) { + var id = getGridIdentifier(g) + var type = getGridType(g.info.grp_codes, id.substring(0, 4)) + var content = [] + content.push(ml("div", {class: "row"}, + ml("div", {class: "col head p-2 mt-3"}, + ml("div", {class: "col a-c"}, type + " (Code " + id + ")") + ) + )) + content.push(ml("div", {class: "row my-2"}, [ + ml("div", {class: "col-4"}, ml("b", {}, "Name")), + ml("div", {class: "col-3"}, ml("b", {}, "Value")), + ml("div", {class: "col-3"}, ml("b", {}, "Range")), + ml("div", {class: "col-2"}, ml("b", {}, "Default")) + ])) + for(e of g.info.group) { + if(Array.isArray(e[id])) { + for(e of e[id]) { + var v = String(getGridValue(g) / e.div); + var vt = (v !== String(e.def)) ? "b" : "span"; + content.push(ml("div", {class: "row mt-2"}, [ + ml("div", {class: "col-4"}, e.name), + ml("div", {class: "col-3"}, ml(vt, {}, v + ((undefined !== e.unit) ? " [" + e.unit + "]" : ""))), + ml("div", {class: "col-3"}, (undefined !== e.min) ? (e.min + " - " + e.max) : "n/a"), + ml("div", {class: "col-2"}, String(e.def)) + ])) + } + } + } + + return ml("div", {class: "col"}, [...content]) } function showGridProfile(obj) { - var html = ml("pre", {}, obj.grid); - modal("Grid Profile for inverter " + obj.name, ml("div", {}, html)); + getJSON("/grid_info.json").then(data => { + var glob = {offs:0, grid:obj.grid, info: data} + var content = []; + content.push(ml("div", {class: "row"}, + ml("div", {class: "col my-3"}, ml("h5", {}, getGridType(glob.info.type, getGridIdentifier(glob)) + " (Version " + getGridValue(glob).toString(16) + ")")) + )) + + while((glob.offs*3) < glob.grid.length) { + content.push(parseGridGroup(glob)) + } + + modal("Grid Profile for inverter " + obj.name, ml("div", {}, ml("div", {class: "col mb-2"}, [...content]))) + }) } @@ -315,8 +378,8 @@ tr2(["RX fragments", obj.frame_cnt, ""]), tr2(["TX retransmits", obj.retransmits, ""]) ]) - ]); - modal("Radio statistics for inverter " + obj.name, ml("div", {}, html)); + ]) + modal("Radio statistics for inverter " + obj.name, ml("div", {}, html)) } function limitModal(obj) { diff --git a/src/web/web.h b/src/web/web.h index b104e9fd..451dc656 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -25,6 +25,7 @@ #include "html/h/colorBright_css.h" #include "html/h/colorDark_css.h" #include "html/h/favicon_ico.h" +#include "html/h/grid_info_json.h" #include "html/h/index_html.h" #include "html/h/login_html.h" #include "html/h/serial_html.h" @@ -65,6 +66,7 @@ class Web { mWeb.on("/colors.css", HTTP_GET, std::bind(&Web::onColor, this, std::placeholders::_1)); mWeb.on("/style.css", HTTP_GET, std::bind(&Web::onCss, this, std::placeholders::_1)); mWeb.on("/api.js", HTTP_GET, std::bind(&Web::onApiJs, this, std::placeholders::_1)); + mWeb.on("/grid_info.json", HTTP_GET, std::bind(&Web::onGridInfoJson, this, std::placeholders::_1)); mWeb.on("/favicon.ico", HTTP_GET, std::bind(&Web::onFavicon, this, std::placeholders::_1)); mWeb.onNotFound ( std::bind(&Web::showNotFound, this, std::placeholders::_1)); mWeb.on("/reboot", HTTP_ANY, std::bind(&Web::onReboot, this, std::placeholders::_1)); @@ -389,6 +391,16 @@ class Web { request->send(response); } + void onGridInfoJson(AsyncWebServerRequest *request) { + DPRINTLN(DBG_VERBOSE, F("onGridInfoJson")); + + AsyncWebServerResponse *response = request->beginResponse_P(200, F("application/json; charset=utf-8"), grid_info_json, grid_info_json_len); + response->addHeader(F("Content-Encoding"), "gzip"); + if(request->hasParam("v")) + response->addHeader(F("Cache-Control"), F("max-age=604800")); + request->send(response); + } + void onFavicon(AsyncWebServerRequest *request) { static const char favicon_type[] PROGMEM = "image/x-icon"; AsyncWebServerResponse *response = request->beginResponse_P(200, favicon_type, favicon_ico, favicon_ico_len);