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);