mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-25 14:56:11 +02:00
improved html and navi, navi is visible even when API dies #660
reduced maximum allowed JSON size for API to 6000Bytes #660 small fix: output command at `prepareDevInformCmd` #692 improved inverter handling for MQTT #671
This commit is contained in:
parent
9ef2df21fa
commit
4f0d365211
21 changed files with 279 additions and 377 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@
|
||||||
.vscode/extensions.json
|
.vscode/extensions.json
|
||||||
src/config/config_override.h
|
src/config/config_override.h
|
||||||
src/web/html/h/*
|
src/web/html/h/*
|
||||||
|
src/web/html/tmp/*
|
||||||
/**/Debug
|
/**/Debug
|
||||||
/**/v16/*
|
/**/v16/*
|
||||||
*.db
|
*.db
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
(starting from release version `0.5.66`)
|
(starting from release version `0.5.66`)
|
||||||
|
|
||||||
|
## 0.5.91
|
||||||
|
* improved html and navi, navi is visible even when API dies #660
|
||||||
|
* reduced maximum allowed JSON size for API to 6000Bytes #660
|
||||||
|
* small fix: output command at `prepareDevInformCmd` #692
|
||||||
|
* improved inverter handling #671
|
||||||
|
|
||||||
## 0.5.90
|
## 0.5.90
|
||||||
* merged PR #684, #698, #705
|
* merged PR #684, #698, #705
|
||||||
* webserial minor overflow fix #660
|
* webserial minor overflow fix #660
|
||||||
|
|
15
src/app.cpp
15
src/app.cpp
|
@ -21,12 +21,6 @@ void app::setup() {
|
||||||
|
|
||||||
resetSystem();
|
resetSystem();
|
||||||
|
|
||||||
/*DBGPRINTLN("--- start");
|
|
||||||
DBGPRINTLN(String(ESP.getFreeHeap()));
|
|
||||||
DBGPRINTLN(String(ESP.getHeapFragmentation()));
|
|
||||||
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
|
|
||||||
|
|
||||||
|
|
||||||
mSettings.setup();
|
mSettings.setup();
|
||||||
mSettings.getPtr(mConfig);
|
mSettings.getPtr(mConfig);
|
||||||
DPRINT(DBG_INFO, F("Settings valid: "));
|
DPRINT(DBG_INFO, F("Settings valid: "));
|
||||||
|
@ -50,6 +44,7 @@ void app::setup() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mSys.addInverters(&mConfig->inst);
|
mSys.addInverters(&mConfig->inst);
|
||||||
|
|
||||||
mPayload.setup(this, &mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
|
mPayload.setup(this, &mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
|
||||||
mPayload.enableSerialDebug(mConfig->serial.debug);
|
mPayload.enableSerialDebug(mConfig->serial.debug);
|
||||||
mPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1));
|
mPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1));
|
||||||
|
@ -57,10 +52,10 @@ void app::setup() {
|
||||||
mMiPayload.setup(this, &mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
|
mMiPayload.setup(this, &mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
|
||||||
mMiPayload.enableSerialDebug(mConfig->serial.debug);
|
mMiPayload.enableSerialDebug(mConfig->serial.debug);
|
||||||
|
|
||||||
/*DBGPRINTLN("--- after payload");
|
DBGPRINTLN("--- after payload");
|
||||||
DBGPRINTLN(String(ESP.getFreeHeap()));
|
DBGPRINTLN(String(ESP.getFreeHeap()));
|
||||||
DBGPRINTLN(String(ESP.getHeapFragmentation()));
|
DBGPRINTLN(String(ESP.getHeapFragmentation()));
|
||||||
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
|
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));
|
||||||
|
|
||||||
if(!mSys.Radio.isChipConnected())
|
if(!mSys.Radio.isChipConnected())
|
||||||
DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring"));
|
DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring"));
|
||||||
|
@ -90,10 +85,10 @@ void app::setup() {
|
||||||
regularTickers();
|
regularTickers();
|
||||||
|
|
||||||
|
|
||||||
/*DBGPRINTLN("--- end setup");
|
DBGPRINTLN("--- end setup");
|
||||||
DBGPRINTLN(String(ESP.getFreeHeap()));
|
DBGPRINTLN(String(ESP.getFreeHeap()));
|
||||||
DBGPRINTLN(String(ESP.getHeapFragmentation()));
|
DBGPRINTLN(String(ESP.getHeapFragmentation()));
|
||||||
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
|
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -250,7 +250,7 @@ class settings {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicJsonDocument json(4500);
|
DynamicJsonDocument json(5500);
|
||||||
JsonObject root = json.to<JsonObject>();
|
JsonObject root = json.to<JsonObject>();
|
||||||
jsonWifi(root.createNestedObject(F("wifi")), true);
|
jsonWifi(root.createNestedObject(F("wifi")), true);
|
||||||
jsonNrf(root.createNestedObject(F("nrf")), true);
|
jsonNrf(root.createNestedObject(F("nrf")), true);
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 5
|
#define VERSION_MINOR 5
|
||||||
#define VERSION_PATCH 90
|
#define VERSION_PATCH 91
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -157,7 +157,8 @@ class HmPayload {
|
||||||
uint8_t cmd = iv->getQueuedCmd();
|
uint8_t cmd = iv->getQueuedCmd();
|
||||||
DPRINT(DBG_INFO, F("(#"));
|
DPRINT(DBG_INFO, F("(#"));
|
||||||
DBGPRINT(String(iv->id));
|
DBGPRINT(String(iv->id));
|
||||||
DBGPRINT(F(") prepareDevInformCmd")); // + String(cmd, HEX));
|
DBGPRINT(F(") prepareDevInformCmd 0x"));
|
||||||
|
DBGPRINTLN(String(cmd, HEX));
|
||||||
mSys->Radio.prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false);
|
mSys->Radio.prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false);
|
||||||
mPayload[iv->id].txCmd = cmd;
|
mPayload[iv->id].txCmd = cmd;
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,7 +406,7 @@ class PubMqtt {
|
||||||
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId];
|
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool processIvStatus() {
|
bool processIvStatus() {
|
||||||
// returns true if any inverter is available
|
// returns true if any inverter is available
|
||||||
bool allAvail = true; // shows if all enabled inverters are available
|
bool allAvail = true; // shows if all enabled inverters are available
|
||||||
bool anyAvail = false; // shows if at least one enabled inverter is available
|
bool anyAvail = false; // shows if at least one enabled inverter is available
|
||||||
|
@ -419,17 +419,19 @@ class PubMqtt {
|
||||||
iv = mSys->getInverterByPos(id);
|
iv = mSys->getInverterByPos(id);
|
||||||
if (NULL == iv)
|
if (NULL == iv)
|
||||||
continue; // skip to next inverter
|
continue; // skip to next inverter
|
||||||
|
if (!iv->config->enabled)
|
||||||
|
continue; // skip to next inverter
|
||||||
|
|
||||||
rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
|
|
||||||
// inverter status
|
// inverter status
|
||||||
uint8_t status = MQTT_STATUS_NOT_AVAIL_NOT_PROD;
|
uint8_t status = MQTT_STATUS_NOT_AVAIL_NOT_PROD;
|
||||||
if (iv->config->enabled) {
|
if (iv->isAvailable(*mUtcTimestamp)) {
|
||||||
if (iv->isAvailable(*mUtcTimestamp))
|
anyAvail = true;
|
||||||
status = (iv->isProducing(*mUtcTimestamp)) ? MQTT_STATUS_AVAIL_PROD : MQTT_STATUS_AVAIL_NOT_PROD;
|
status = (iv->isProducing(*mUtcTimestamp)) ? MQTT_STATUS_AVAIL_PROD : MQTT_STATUS_AVAIL_NOT_PROD;
|
||||||
else // inverter is enabled but not available
|
|
||||||
allAvail = false;
|
|
||||||
}
|
}
|
||||||
|
else // inverter is enabled but not available
|
||||||
|
allAvail = false;
|
||||||
|
|
||||||
if(mLastIvState[id] != status) {
|
if(mLastIvState[id] != status) {
|
||||||
// if status changed from producing to not producing send last data immediately
|
// if status changed from producing to not producing send last data immediately
|
||||||
|
@ -439,11 +441,11 @@ class PubMqtt {
|
||||||
mLastIvState[id] = status;
|
mLastIvState[id] = status;
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|
||||||
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/%s", iv->config->name, mqttStr[MQTT_STR_AVAILABLE]);
|
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available", iv->config->name);
|
||||||
snprintf(val, 40, "%d", status);
|
snprintf(val, 40, "%d", status);
|
||||||
publish(topic, val, true);
|
publish(topic, val, true);
|
||||||
|
|
||||||
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/%s", iv->config->name, mqttStr[MQTT_STR_LAST_SUCCESS]);
|
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->config->name);
|
||||||
snprintf(val, 40, "%d", iv->getLastTs(rec));
|
snprintf(val, 40, "%d", iv->getLastTs(rec));
|
||||||
publish(topic, val, true);
|
publish(topic, val, true);
|
||||||
}
|
}
|
||||||
|
@ -451,7 +453,7 @@ class PubMqtt {
|
||||||
|
|
||||||
if(changed) {
|
if(changed) {
|
||||||
snprintf(val, 32, "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((anyAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE)));
|
snprintf(val, 32, "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((anyAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE)));
|
||||||
publish(subtopics[MQTT_STATUS], val, true);
|
publish("status", val, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return anyAvail;
|
return anyAvail;
|
||||||
|
@ -474,24 +476,26 @@ class PubMqtt {
|
||||||
char topic[7 + MQTT_TOPIC_LEN], val[40];
|
char topic[7 + MQTT_TOPIC_LEN], val[40];
|
||||||
record_t<> *rec = iv->getRecordStruct(curInfoCmd);
|
record_t<> *rec = iv->getRecordStruct(curInfoCmd);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < rec->length; i++) {
|
if (iv->getLastTs(rec) > 0) {
|
||||||
bool retained = false;
|
for (uint8_t i = 0; i < rec->length; i++) {
|
||||||
if (curInfoCmd == RealTimeRunData_Debug) {
|
bool retained = false;
|
||||||
switch (rec->assign[i].fieldId) {
|
if (curInfoCmd == RealTimeRunData_Debug) {
|
||||||
case FLD_YT:
|
switch (rec->assign[i].fieldId) {
|
||||||
case FLD_YD:
|
case FLD_YT:
|
||||||
if ((rec->assign[i].ch == CH0) && (!iv->isProducing(*mUtcTimestamp))) // avoids returns to 0 on restart
|
case FLD_YD:
|
||||||
continue;
|
if ((rec->assign[i].ch == CH0) && (!iv->isProducing(*mUtcTimestamp))) // avoids returns to 0 on restart
|
||||||
retained = true;
|
continue;
|
||||||
break;
|
retained = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]);
|
||||||
|
snprintf(val, 40, "%g", ah::round3(iv->getValue(i, rec)));
|
||||||
|
publish(topic, val, retained);
|
||||||
|
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]);
|
|
||||||
snprintf(val, 40, "%g", ah::round3(iv->getValue(i, rec)));
|
|
||||||
publish(topic, val, retained);
|
|
||||||
|
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,42 +516,49 @@ class PubMqtt {
|
||||||
uint8_t curInfoCmd = mSendList.front();
|
uint8_t curInfoCmd = mSendList.front();
|
||||||
|
|
||||||
if ((curInfoCmd != RealTimeRunData_Debug) || !RTRDataHasBeenSent) { // send RTR Data only once
|
if ((curInfoCmd != RealTimeRunData_Debug) || !RTRDataHasBeenSent) { // send RTR Data only once
|
||||||
|
bool sendTotals = (curInfoCmd == RealTimeRunData_Debug);
|
||||||
|
|
||||||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if (NULL == iv)
|
if (NULL == iv)
|
||||||
continue; // skip to next inverter
|
continue; // skip to next inverter
|
||||||
|
if (!iv->config->enabled)
|
||||||
|
continue; // skip to next inverter
|
||||||
|
|
||||||
// send RTR Data only if status is available
|
// send RTR Data only if status is available
|
||||||
if ((curInfoCmd != RealTimeRunData_Debug) || (MQTT_STATUS_AVAIL_PROD == mLastIvState[id]))
|
if ((curInfoCmd != RealTimeRunData_Debug) || (MQTT_STATUS_NOT_AVAIL_NOT_PROD != mLastIvState[id]))
|
||||||
sendData(iv, curInfoCmd);
|
sendData(iv, curInfoCmd);
|
||||||
|
|
||||||
// calculate total values for RealTimeRunData_Debug
|
// calculate total values for RealTimeRunData_Debug
|
||||||
if (curInfoCmd == RealTimeRunData_Debug) {
|
if (sendTotals) {
|
||||||
record_t<> *rec = iv->getRecordStruct(curInfoCmd);
|
record_t<> *rec = iv->getRecordStruct(curInfoCmd);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < rec->length; i++) {
|
sendTotals &= (iv->getLastTs(rec) > 0);
|
||||||
if (CH0 == rec->assign[i].ch) {
|
if (sendTotals) {
|
||||||
switch (rec->assign[i].fieldId) {
|
for (uint8_t i = 0; i < rec->length; i++) {
|
||||||
case FLD_PAC:
|
if (CH0 == rec->assign[i].ch) {
|
||||||
total[0] += iv->getValue(i, rec);
|
switch (rec->assign[i].fieldId) {
|
||||||
break;
|
case FLD_PAC:
|
||||||
case FLD_YT:
|
total[0] += iv->getValue(i, rec);
|
||||||
total[1] += iv->getValue(i, rec);
|
break;
|
||||||
break;
|
case FLD_YT:
|
||||||
case FLD_YD:
|
total[1] += iv->getValue(i, rec);
|
||||||
total[2] += iv->getValue(i, rec);
|
break;
|
||||||
break;
|
case FLD_YD:
|
||||||
case FLD_PDC:
|
total[2] += iv->getValue(i, rec);
|
||||||
total[3] += iv->getValue(i, rec);
|
break;
|
||||||
break;
|
case FLD_PDC:
|
||||||
|
total[3] += iv->getValue(i, rec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curInfoCmd == RealTimeRunData_Debug) {
|
if (sendTotals) {
|
||||||
uint8_t fieldId;
|
uint8_t fieldId;
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
|
@ -565,7 +576,7 @@ class PubMqtt {
|
||||||
fieldId = FLD_PDC;
|
fieldId = FLD_PDC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/%s", mqttStr[MQTT_STR_TOTAL], fields[fieldId]);
|
snprintf(topic, 32 + MAX_NAME_LENGTH, "total/%s", fields[fieldId]);
|
||||||
snprintf(val, 40, "%g", ah::round3(total[i]));
|
snprintf(val, 40, "%g", ah::round3(total[i]));
|
||||||
publish(topic, val, true);
|
publish(topic, val, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ class RestApi {
|
||||||
mHeapFrag = ESP.getHeapFragmentation();
|
mHeapFrag = ESP.getHeapFragmentation();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192);
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, 6000);
|
||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
|
|
||||||
String path = request->url().substring(5);
|
String path = request->url().substring(5);
|
||||||
|
@ -83,7 +83,6 @@ class RestApi {
|
||||||
else if(path == "reboot") getReboot(root);
|
else if(path == "reboot") getReboot(root);
|
||||||
else if(path == "statistics") getStatistics(root);
|
else if(path == "statistics") getStatistics(root);
|
||||||
else if(path == "inverter/list") getInverterList(root);
|
else if(path == "inverter/list") getInverterList(root);
|
||||||
else if(path == "menu") getMenu(root);
|
|
||||||
else if(path == "index") getIndex(root);
|
else if(path == "index") getIndex(root);
|
||||||
else if(path == "setup") getSetup(root);
|
else if(path == "setup") getSetup(root);
|
||||||
else if(path == "setup/networks") getNetworks(root);
|
else if(path == "setup/networks") getNetworks(root);
|
||||||
|
@ -183,10 +182,13 @@ class RestApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getGeneric(JsonObject obj) {
|
void getGeneric(JsonObject obj) {
|
||||||
obj[F("version")] = String(mApp->getVersion());
|
|
||||||
obj[F("build")] = String(AUTO_GIT_HASH);
|
obj[F("build")] = String(AUTO_GIT_HASH);
|
||||||
obj[F("wifi_rssi")] = (WiFi.status() != WL_CONNECTED) ? 0 : WiFi.RSSI();
|
obj[F("wifi_rssi")] = (WiFi.status() != WL_CONNECTED) ? 0 : WiFi.RSSI();
|
||||||
obj[F("ts_uptime")] = mApp->getUptime();
|
obj[F("ts_uptime")] = mApp->getUptime();
|
||||||
|
obj[F("menu_prot")] = mApp->getProtection();
|
||||||
|
obj[F("menu_maskH")] = ((mConfig->sys.protectionMask >> 8) & 0xff);
|
||||||
|
obj[F("menu_maskL")] = ((mConfig->sys.protectionMask ) & 0xff);
|
||||||
|
obj[F("menu_protEn")] = (bool) (strlen(mConfig->sys.adminPwd) > 0);
|
||||||
|
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
obj[F("esp_type")] = F("ESP32");
|
obj[F("esp_type")] = F("ESP32");
|
||||||
|
@ -244,7 +246,6 @@ class RestApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getHtmlSystem(JsonObject obj) {
|
void getHtmlSystem(JsonObject obj) {
|
||||||
getMenu(obj.createNestedObject(F("menu")));
|
|
||||||
getSysInfo(obj.createNestedObject(F("system")));
|
getSysInfo(obj.createNestedObject(F("system")));
|
||||||
getGeneric(obj.createNestedObject(F("generic")));
|
getGeneric(obj.createNestedObject(F("generic")));
|
||||||
obj[F("html")] = F("<a href=\"/factory\" class=\"btn\">Factory Reset</a><br/><br/><a href=\"/reboot\" class=\"btn\">Reboot</a>");
|
obj[F("html")] = F("<a href=\"/factory\" class=\"btn\">Factory Reset</a><br/><br/><a href=\"/reboot\" class=\"btn\">Reboot</a>");
|
||||||
|
@ -252,7 +253,6 @@ class RestApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getHtmlLogout(JsonObject obj) {
|
void getHtmlLogout(JsonObject obj) {
|
||||||
getMenu(obj.createNestedObject(F("menu")));
|
|
||||||
getGeneric(obj.createNestedObject(F("generic")));
|
getGeneric(obj.createNestedObject(F("generic")));
|
||||||
obj[F("refresh")] = 3;
|
obj[F("refresh")] = 3;
|
||||||
obj[F("refresh_url")] = "/";
|
obj[F("refresh_url")] = "/";
|
||||||
|
@ -260,7 +260,6 @@ class RestApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getHtmlSave(JsonObject obj) {
|
void getHtmlSave(JsonObject obj) {
|
||||||
getMenu(obj.createNestedObject(F("menu")));
|
|
||||||
getGeneric(obj.createNestedObject(F("generic")));
|
getGeneric(obj.createNestedObject(F("generic")));
|
||||||
obj[F("refresh")] = 2;
|
obj[F("refresh")] = 2;
|
||||||
obj[F("refresh_url")] = "/setup";
|
obj[F("refresh_url")] = "/setup";
|
||||||
|
@ -268,7 +267,6 @@ class RestApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getReboot(JsonObject obj) {
|
void getReboot(JsonObject obj) {
|
||||||
getMenu(obj.createNestedObject(F("menu")));
|
|
||||||
getGeneric(obj.createNestedObject(F("generic")));
|
getGeneric(obj.createNestedObject(F("generic")));
|
||||||
obj[F("refresh")] = 10;
|
obj[F("refresh")] = 10;
|
||||||
obj[F("refresh_url")] = "/";
|
obj[F("refresh_url")] = "/";
|
||||||
|
@ -377,54 +375,9 @@ class RestApi {
|
||||||
obj[F("pinDisp1")] = mConfig->plugin.display.pin1;
|
obj[F("pinDisp1")] = mConfig->plugin.display.pin1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getMenu(JsonObject obj) {
|
|
||||||
uint8_t i = 0;
|
|
||||||
uint16_t mask = (mApp->getProtection()) ? mConfig->sys.protectionMask : 0;
|
|
||||||
if(!CHECK_MASK(mask, PROT_MASK_LIVE)) {
|
|
||||||
obj[F("name")][i] = "Live";
|
|
||||||
obj[F("link")][i++] = "/live";
|
|
||||||
}
|
|
||||||
if(!CHECK_MASK(mask, PROT_MASK_SERIAL)) {
|
|
||||||
obj[F("name")][i] = "Serial / Control";
|
|
||||||
obj[F("link")][i++] = "/serial";
|
|
||||||
}
|
|
||||||
if(!CHECK_MASK(mask, PROT_MASK_SETUP)) {
|
|
||||||
obj[F("name")][i] = "Settings";
|
|
||||||
obj[F("link")][i++] = "/setup";
|
|
||||||
}
|
|
||||||
obj[F("name")][i++] = "-";
|
|
||||||
obj[F("name")][i] = "REST API";
|
|
||||||
obj[F("link")][i] = "/api";
|
|
||||||
obj[F("trgt")][i++] = "_blank";
|
|
||||||
obj[F("name")][i++] = "-";
|
|
||||||
if(!CHECK_MASK(mask, PROT_MASK_UPDATE)) {
|
|
||||||
obj[F("name")][i] = "Update";
|
|
||||||
obj[F("link")][i++] = "/update";
|
|
||||||
}
|
|
||||||
if(!CHECK_MASK(mask, PROT_MASK_SYSTEM)) {
|
|
||||||
obj[F("name")][i] = "System";
|
|
||||||
obj[F("link")][i++] = "/system";
|
|
||||||
}
|
|
||||||
obj[F("name")][i++] = "-";
|
|
||||||
obj[F("name")][i] = "Documentation";
|
|
||||||
obj[F("link")][i] = "https://ahoydtu.de";
|
|
||||||
obj[F("trgt")][i++] = "_blank";
|
|
||||||
if(strlen(mConfig->sys.adminPwd) > 0) {
|
|
||||||
obj[F("name")][i++] = "-";
|
|
||||||
if(mApp->getProtection()) {
|
|
||||||
obj[F("name")][i] = "Login";
|
|
||||||
obj[F("link")][i++] = "/login";
|
|
||||||
} else {
|
|
||||||
obj[F("name")][i] = "Logout";
|
|
||||||
obj[F("link")][i++] = "/logout";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void getIndex(JsonObject obj) {
|
void getIndex(JsonObject obj) {
|
||||||
getMenu(obj.createNestedObject(F("menu")));
|
|
||||||
getGeneric(obj.createNestedObject(F("generic")));
|
getGeneric(obj.createNestedObject(F("generic")));
|
||||||
|
|
||||||
obj[F("ts_now")] = mApp->getTimestamp();
|
obj[F("ts_now")] = mApp->getTimestamp();
|
||||||
obj[F("ts_sunrise")] = mApp->getSunrise();
|
obj[F("ts_sunrise")] = mApp->getSunrise();
|
||||||
obj[F("ts_sunset")] = mApp->getSunset();
|
obj[F("ts_sunset")] = mApp->getSunset();
|
||||||
|
@ -473,10 +426,9 @@ class RestApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSetup(JsonObject obj) {
|
void getSetup(JsonObject obj) {
|
||||||
getMenu(obj.createNestedObject(F("menu")));
|
|
||||||
getGeneric(obj.createNestedObject(F("generic")));
|
getGeneric(obj.createNestedObject(F("generic")));
|
||||||
getSysInfo(obj.createNestedObject(F("system")));
|
getSysInfo(obj.createNestedObject(F("system")));
|
||||||
getInverterList(obj.createNestedObject(F("inverter")));
|
//getInverterList(obj.createNestedObject(F("inverter")));
|
||||||
getMqtt(obj.createNestedObject(F("mqtt")));
|
getMqtt(obj.createNestedObject(F("mqtt")));
|
||||||
getNtp(obj.createNestedObject(F("ntp")));
|
getNtp(obj.createNestedObject(F("ntp")));
|
||||||
getSun(obj.createNestedObject(F("sun")));
|
getSun(obj.createNestedObject(F("sun")));
|
||||||
|
@ -492,7 +444,6 @@ class RestApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getLive(JsonObject obj) {
|
void getLive(JsonObject obj) {
|
||||||
getMenu(obj.createNestedObject(F("menu")));
|
|
||||||
getGeneric(obj.createNestedObject(F("generic")));
|
getGeneric(obj.createNestedObject(F("generic")));
|
||||||
JsonArray invArr = obj.createNestedArray(F("inverter"));
|
JsonArray invArr = obj.createNestedArray(F("inverter"));
|
||||||
obj["refresh_interval"] = mConfig->nrf.sendInterval;
|
obj["refresh_interval"] = mConfig->nrf.sendInterval;
|
||||||
|
|
|
@ -38,18 +38,21 @@ function topnav() {
|
||||||
toggle("topnav");
|
toggle("topnav");
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseMenu(obj) {
|
function parseNav(obj) {
|
||||||
var e = document.getElementById("topnav");
|
for(i = 0; i < 7; i++) {
|
||||||
e.innerHTML = "";
|
var l = document.getElementById("nav"+i);
|
||||||
for(var i = 0; i < obj["name"].length; i ++) {
|
if(window.location.pathname == "/" + l.href.split('/').pop())
|
||||||
if(obj["name"][i] == "-")
|
l.classList.add("active");
|
||||||
e.appendChild(span("", ["seperator"]));
|
|
||||||
else {
|
if(obj["menu_protEn"]) {
|
||||||
var l = link(obj["link"][i], obj["name"][i], obj["trgt"][i]);
|
if(obj["menu_prot"]) {
|
||||||
if(obj["link"][i] == window.location.pathname)
|
if((((obj["menu_mask"] >> i) & 0x01) == 0x01) || (1 == i))
|
||||||
l.classList.add("active");
|
l.classList.remove("hide");
|
||||||
e.appendChild(l);
|
|
||||||
}
|
} else if(0 == i)
|
||||||
|
l.classList.remove("hide");
|
||||||
|
} else if(i > 1)
|
||||||
|
l.classList.remove("hide");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,82 @@ import re
|
||||||
import os
|
import os
|
||||||
import gzip
|
import gzip
|
||||||
import glob
|
import glob
|
||||||
|
import shutil
|
||||||
|
import pkg_resources
|
||||||
|
from datetime import date
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from dulwich import porcelain
|
||||||
|
|
||||||
def convert2Header(inFile):
|
required_pkgs = {'dulwich'}
|
||||||
|
installed_pkgs = {pkg.key for pkg in pkg_resources.working_set}
|
||||||
|
missing_pkgs = required_pkgs - installed_pkgs
|
||||||
|
|
||||||
|
if missing_pkgs:
|
||||||
|
env.Execute('"$PYTHONEXE" -m pip install dulwich')
|
||||||
|
|
||||||
|
|
||||||
|
def get_git_sha():
|
||||||
|
try:
|
||||||
|
build_version = porcelain.describe('../../../') # refers to the repository root dir
|
||||||
|
except:
|
||||||
|
build_version = "g0000000"
|
||||||
|
|
||||||
|
build_flag = "-D AUTO_GIT_HASH=\\\"" + build_version[1:] + "\\\""
|
||||||
|
#print ("Firmware Revision: " + build_version)
|
||||||
|
return (build_flag)
|
||||||
|
|
||||||
|
def readVersion(path):
|
||||||
|
f = open(path, "r")
|
||||||
|
lines = f.readlines()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
today = date.today()
|
||||||
|
search = ["_MAJOR", "_MINOR", "_PATCH"]
|
||||||
|
version = today.strftime("%y%m%d") + "_ahoy_"
|
||||||
|
ver = ""
|
||||||
|
for line in lines:
|
||||||
|
if(line.find("VERSION_") != -1):
|
||||||
|
for s in search:
|
||||||
|
p = line.find(s)
|
||||||
|
if(p != -1):
|
||||||
|
version += line[p+13:].rstrip() + "."
|
||||||
|
ver += line[p+13:].rstrip() + "."
|
||||||
|
return ver[:-1]
|
||||||
|
|
||||||
|
def htmlParts(file, header, nav, footer, version):
|
||||||
|
p = "";
|
||||||
|
f = open(file, "r")
|
||||||
|
lines = f.readlines()
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
f = open(header, "r")
|
||||||
|
h = f.read().strip()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = open(nav, "r")
|
||||||
|
n = f.read().strip()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = open(footer, "r")
|
||||||
|
fo = f.read().strip()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line = line.replace("{#HTML_HEADER}", h)
|
||||||
|
line = line.replace("{#HTML_NAV}", n)
|
||||||
|
line = line.replace("{#HTML_FOOTER}", fo)
|
||||||
|
p += line
|
||||||
|
|
||||||
|
#placeholders
|
||||||
|
link = '<a target="_blank" href="https://github.com/lumapu/ahoy/commits/' + get_git_sha() + '">GIT SHA: ' + get_git_sha() + ' :: ' + version + '</a>'
|
||||||
|
p = p.replace("{#VERSION}", version)
|
||||||
|
p = p.replace("{#VERSION_GIT}", link)
|
||||||
|
f = open("tmp/" + file, "w")
|
||||||
|
f.write(p);
|
||||||
|
f.close();
|
||||||
|
return p
|
||||||
|
|
||||||
|
def convert2Header(inFile, version):
|
||||||
fileType = inFile.split(".")[1]
|
fileType = inFile.split(".")[1]
|
||||||
define = inFile.split(".")[0].upper()
|
define = inFile.split(".")[0].upper()
|
||||||
define2 = inFile.split(".")[1].upper()
|
define2 = inFile.split(".")[1].upper()
|
||||||
|
@ -17,14 +89,19 @@ def convert2Header(inFile):
|
||||||
Path("html/h").mkdir(exist_ok=True)
|
Path("html/h").mkdir(exist_ok=True)
|
||||||
else:
|
else:
|
||||||
outName = "h/" + inFileVarName + ".h"
|
outName = "h/" + inFileVarName + ".h"
|
||||||
Path("h").mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
|
data = ""
|
||||||
if fileType == "ico":
|
if fileType == "ico":
|
||||||
f = open(inFile, "rb")
|
f = open(inFile, "rb")
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
else:
|
else:
|
||||||
f = open(inFile, "r")
|
if fileType == "html":
|
||||||
data = f.read()
|
data = htmlParts(inFile, "includes/header.html", "includes/nav.html", "includes/footer.html", version)
|
||||||
f.close()
|
else:
|
||||||
|
f = open(inFile, "r")
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
if fileType == "css":
|
if fileType == "css":
|
||||||
data = data.replace('\n', '')
|
data = data.replace('\n', '')
|
||||||
|
@ -53,13 +130,17 @@ def convert2Header(inFile):
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# delete all files in the 'h' dir
|
# delete all files in the 'h' dir
|
||||||
dir = 'h'
|
wd = 'h'
|
||||||
if os.getcwd()[-4:] != "html":
|
if os.getcwd()[-4:] != "html":
|
||||||
dir = "web/html/" + dir
|
wd = "web/html/" + wd
|
||||||
|
|
||||||
if os.path.exists(dir):
|
if os.path.exists(wd):
|
||||||
for f in os.listdir(dir):
|
for f in os.listdir(wd):
|
||||||
os.remove(os.path.join(dir, f))
|
os.remove(os.path.join(wd, f))
|
||||||
|
wd += "/tmp"
|
||||||
|
if os.path.exists(wd):
|
||||||
|
for f in os.listdir(wd):
|
||||||
|
os.remove(os.path.join(wd, f))
|
||||||
|
|
||||||
# grab all files with following extensions
|
# grab all files with following extensions
|
||||||
if os.getcwd()[-4:] != "html":
|
if os.getcwd()[-4:] != "html":
|
||||||
|
@ -69,6 +150,11 @@ files_grabbed = []
|
||||||
for files in types:
|
for files in types:
|
||||||
files_grabbed.extend(glob.glob(files))
|
files_grabbed.extend(glob.glob(files))
|
||||||
|
|
||||||
|
Path("h").mkdir(exist_ok=True)
|
||||||
|
Path("tmp").mkdir(exist_ok=True) # created to check if webpages are valid with all replacements
|
||||||
|
shutil.copyfile("style.css", "tmp/style.css")
|
||||||
|
version = readVersion("../../defines.h")
|
||||||
|
|
||||||
# go throw the array
|
# go throw the array
|
||||||
for val in files_grabbed:
|
for val in files_grabbed:
|
||||||
convert2Header(val)
|
convert2Header(val, version)
|
||||||
|
|
16
src/web/html/includes/footer.html
Normal file
16
src/web/html/includes/footer.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<div id="footer">
|
||||||
|
<div class="left">
|
||||||
|
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
||||||
|
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<ul>
|
||||||
|
<li>{#VERSION}</li>
|
||||||
|
<li><span id="esp_type"></span></li>
|
||||||
|
<li><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
3
src/web/html/includes/header.html
Normal file
3
src/web/html/includes/header.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<script type="text/javascript" src="api.js"></script>
|
27
src/web/html/includes/nav.html
Normal file
27
src/web/html/includes/nav.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<div class="topnav">
|
||||||
|
<a href="/" class="title">AhoyDTU</a>
|
||||||
|
<a href="javascript:void(0);" class="icon" onclick="topnav()">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</a>
|
||||||
|
<div id="topnav" class="mobile">
|
||||||
|
<div id="topnav" class="mobile">
|
||||||
|
<a id="nav2" class="hide" href="/live">Live</a>
|
||||||
|
<a id="nav3" class="hide" href="/serial">Serial / Control</a>
|
||||||
|
<a id="nav4" class="hide" href="/setup">Settings</a>
|
||||||
|
<span class="seperator"></span>
|
||||||
|
<a id="nav5" class="hide" href="/update">Update</a>
|
||||||
|
<a id="nav6" class="hide" href="/system">System</a>
|
||||||
|
<span class="seperator"></span>
|
||||||
|
<a id="nav7" href="/api" target="_blank">REST API</a>
|
||||||
|
<a id="nav8" href="https://ahoydtu.de" target="_blank">Documentation</a>
|
||||||
|
<span class="seperator"></span>
|
||||||
|
<a id="nav0" class="hide" href="/login">Login</a>
|
||||||
|
<a id="nav1" class="hide" href="/logout">Logout</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="wifiicon" class="info"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,36 +2,12 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Index</title>
|
<title>Index</title>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
{#HTML_HEADER}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<script type="text/javascript" src="api.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="topnav">
|
{#HTML_NAV}
|
||||||
<a href="/" class="title">AhoyDTU</a>
|
|
||||||
<a href="javascript:void(0);" class="icon" onclick="topnav()">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</a>
|
|
||||||
<div id="topnav" class="hide"></div>
|
|
||||||
<div id="wifiicon" class="info"></div>
|
|
||||||
</div>
|
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<script>
|
|
||||||
function promptFunction() {
|
|
||||||
var Text = prompt("This project was started from https://www.mikrocontroller.net/topic/525778 this discussion.\n\n" +
|
|
||||||
"The Hoymiles protocol was decrypted through the voluntary efforts of many participants. ahoy, among others, was developed based on this work.\n" +
|
|
||||||
"The software was developed to the best of our knowledge and belief. Nevertheless, no liability can be accepted for a malfunction or guarantee loss of the inverter.\n\n" +
|
|
||||||
"Ahoy is freely available. If you paid money for the software, you probably got ripped off.\n\nPlease type in 'YeS', you are accept our Disclaim. You should then save your config.", "");
|
|
||||||
if (Text != "YeS")
|
|
||||||
promptFunction();
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<p>
|
<p>
|
||||||
<span class="des">Uptime: </span><span id="uptime"></span><br/>
|
<span class="des">Uptime: </span><span id="uptime"></span><br/>
|
||||||
<span class="des">ESP-Time: </span><span id="date"></span>
|
<span class="des">ESP-Time: </span><span id="date"></span>
|
||||||
|
@ -60,22 +36,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
{#HTML_FOOTER}
|
||||||
<div class="left">
|
|
||||||
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
|
||||||
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<ul>
|
|
||||||
<li><span id="version"></span></li>
|
|
||||||
<li><span id="esp_type"></span></li>
|
|
||||||
<li><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var exeOnce = true;
|
var exeOnce = true;
|
||||||
var tickCnt = 0;
|
var tickCnt = 0;
|
||||||
|
@ -108,10 +69,10 @@
|
||||||
function parseGeneric(obj) {
|
function parseGeneric(obj) {
|
||||||
// Disclaimer
|
// Disclaimer
|
||||||
//if(obj["disclaimer"] == false) sessionStorage.setItem("gDisclaimer", promptFunction());
|
//if(obj["disclaimer"] == false) sessionStorage.setItem("gDisclaimer", promptFunction());
|
||||||
if(exeOnce){
|
/*if(exeOnce){
|
||||||
parseVersion(obj);
|
parseVersion(obj);
|
||||||
parseESP(obj);
|
parseESP(obj);
|
||||||
}
|
}*/
|
||||||
parseRssi(obj);
|
parseRssi(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +164,7 @@
|
||||||
document.getElementById("iv").replaceChildren(p);
|
document.getElementById("iv").replaceChildren(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseWarnInfo(warn, success, version) {
|
function parseWarnInfo(warn, success) {
|
||||||
var p = div(["none"]);
|
var p = div(["none"]);
|
||||||
for(var w of warn) {
|
for(var w of warn) {
|
||||||
p.append(svg(iconWarn, 20, 20, "#F70", "icon"), span(w), br());
|
p.append(svg(iconWarn, 20, 20, "#F70", "icon"), span(w), br());
|
||||||
|
@ -216,10 +177,10 @@
|
||||||
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span(commInfo), br());
|
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span(commInfo), br());
|
||||||
|
|
||||||
if(null != release) {
|
if(null != release) {
|
||||||
if(getVerInt(version) < getVerInt(release))
|
if(getVerInt("{#VERSION}") < getVerInt(release))
|
||||||
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span("Update available, current released version: " + release), br());
|
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span("Update available, current released version: " + release), br());
|
||||||
else if(getVerInt(version) > getVerInt(release))
|
else if(getVerInt("{#VERSION}") > getVerInt(release))
|
||||||
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span("You are using development version " + version +". In case of issues you may want to try the current stable release: " + release), br());
|
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span("You are using development version {#VERSION}. In case of issues you may want to try the current stable release: " + release), br());
|
||||||
else
|
else
|
||||||
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span("You are using the current stable release: " + release), br());
|
p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span("You are using the current stable release: " + release), br());
|
||||||
}
|
}
|
||||||
|
@ -239,11 +200,11 @@
|
||||||
function parse(obj) {
|
function parse(obj) {
|
||||||
if(null != obj) {
|
if(null != obj) {
|
||||||
if(exeOnce)
|
if(exeOnce)
|
||||||
parseMenu(obj["menu"]);
|
parseNav(obj["generic"]);
|
||||||
parseGeneric(obj["generic"]);
|
parseGeneric(obj["generic"]);
|
||||||
parseSys(obj);
|
parseSys(obj);
|
||||||
parseIv(obj["inverter"]);
|
parseIv(obj["inverter"]);
|
||||||
parseWarnInfo(obj["warnings"], obj["infos"], obj["generic"]["version"]);
|
parseWarnInfo(obj["warnings"], obj["infos"]);
|
||||||
if(exeOnce) {
|
if(exeOnce) {
|
||||||
window.setInterval("tick()", 1000);
|
window.setInterval("tick()", 1000);
|
||||||
exeOnce = false;
|
exeOnce = false;
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Login</title>
|
<title>Login</title>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
{#HTML_HEADER}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<script type="text/javascript" src="api.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
|
@ -18,25 +16,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
{#HTML_FOOTER}
|
||||||
<div class="left">
|
|
||||||
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
|
||||||
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<span id="version"></span><br/><br/>
|
|
||||||
<a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function parse(obj) {
|
|
||||||
parseVersion(obj["general"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAjax("/api/generic", parse);
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,21 +2,10 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Serial Console</title>
|
<title>Serial Console</title>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
{#HTML_HEADER}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<script type="text/javascript" src="api.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="topnav">
|
{#HTML_NAV}
|
||||||
<a href="/" class="title">AhoyDTU</a>
|
|
||||||
<a href="javascript:void(0);" class="icon" onclick="topnav()">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</a>
|
|
||||||
<div id="topnav" class="hide"></div>
|
|
||||||
<div id="wifiicon" class="info"></div>
|
|
||||||
</div>
|
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div class="serial">
|
<div class="serial">
|
||||||
|
@ -53,22 +42,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
{#HTML_FOOTER}
|
||||||
<div class="left">
|
|
||||||
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
|
||||||
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<ul>
|
|
||||||
<li><span id="version"></span></li>
|
|
||||||
<li><span id="esp_type"></span></li>
|
|
||||||
<li><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var mAutoScroll = true;
|
var mAutoScroll = true;
|
||||||
var con = document.getElementById("serial");
|
var con = document.getElementById("serial");
|
||||||
|
@ -87,7 +61,7 @@
|
||||||
|
|
||||||
parseRssi(obj);
|
parseRssi(obj);
|
||||||
if(true == exeOnce) {
|
if(true == exeOnce) {
|
||||||
parseVersion(obj);
|
parseNav(obj);
|
||||||
parseESP(obj);
|
parseESP(obj);
|
||||||
window.setInterval("getAjax('/api/generic', parseGeneric)", 10000);
|
window.setInterval("getAjax('/api/generic', parseGeneric)", 10000);
|
||||||
exeOnce = false;
|
exeOnce = false;
|
||||||
|
@ -96,7 +70,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse(root) {
|
function parse(root) {
|
||||||
parseMenu(root["menu"]);
|
|
||||||
select = document.getElementById('InvID');
|
select = document.getElementById('InvID');
|
||||||
|
|
||||||
if(null == root) return;
|
if(null == root) return;
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Setup</title>
|
<title>Setup</title>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
{#HTML_HEADER}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<script type="text/javascript" src="api.js"></script>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function load() {
|
function load() {
|
||||||
for(it of document.getElementsByClassName("s_collapsible")) {
|
for(it of document.getElementsByClassName("s_collapsible")) {
|
||||||
|
@ -18,16 +16,7 @@
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="load()">
|
<body onload="load()">
|
||||||
<div class="topnav">
|
{#HTML_NAV}
|
||||||
<a href="/" class="title">AhoyDTU</a>
|
|
||||||
<a href="javascript:void(0);" class="icon" onclick="topnav()">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</a>
|
|
||||||
<div id="topnav" class="hide"></div>
|
|
||||||
<div id="wifiicon" class="info"></div>
|
|
||||||
</div>
|
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<form method="post" action="/save">
|
<form method="post" action="/save">
|
||||||
|
@ -224,22 +213,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
{#HTML_FOOTER}
|
||||||
<div class="left">
|
|
||||||
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
|
||||||
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<ul>
|
|
||||||
<li><span id="version"></span></li>
|
|
||||||
<li><span id="esp_type"></span></li>
|
|
||||||
<li><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var highestId = 0;
|
var highestId = 0;
|
||||||
var maxInv = 0;
|
var maxInv = 0;
|
||||||
|
@ -426,7 +400,7 @@
|
||||||
|
|
||||||
iv.append(
|
iv.append(
|
||||||
lbl(id + "Name", "Name*"),
|
lbl(id + "Name", "Name*"),
|
||||||
inp(id + "Name", obj["name"], 32, ["text"], null, "text", "[A-Za-z0-9./#$%&=+_-]+", "Invalid input")
|
inp(id + "Name", obj["name"], 16, ["text"], null, "text", "[A-Za-z0-9./#$%&=+_-]+", "Invalid input")
|
||||||
);
|
);
|
||||||
|
|
||||||
for(var j of [
|
for(var j of [
|
||||||
|
@ -479,7 +453,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseGeneric(obj) {
|
function parseGeneric(obj) {
|
||||||
parseVersion(obj);
|
parseNav(obj);
|
||||||
parseESP(obj);
|
parseESP(obj);
|
||||||
parseRssi(obj);
|
parseRssi(obj);
|
||||||
}
|
}
|
||||||
|
@ -568,11 +542,9 @@
|
||||||
|
|
||||||
function parse(root) {
|
function parse(root) {
|
||||||
if(null != root) {
|
if(null != root) {
|
||||||
parseMenu(root["menu"]);
|
|
||||||
parseSys(root["system"]);
|
parseSys(root["system"]);
|
||||||
parseGeneric(root["generic"]);
|
parseGeneric(root["generic"]);
|
||||||
parseStaticIp(root["static_ip"]);
|
parseStaticIp(root["static_ip"]);
|
||||||
parseIv(root["inverter"]);
|
|
||||||
parseMqtt(root["mqtt"]);
|
parseMqtt(root["mqtt"]);
|
||||||
parseNtp(root["ntp"]);
|
parseNtp(root["ntp"]);
|
||||||
parseSun(root["sun"]);
|
parseSun(root["sun"]);
|
||||||
|
@ -580,6 +552,7 @@
|
||||||
parseRadio(root["radio"]);
|
parseRadio(root["radio"]);
|
||||||
parseSerial(root["serial"]);
|
parseSerial(root["serial"]);
|
||||||
parseDisplay(root["display"], root["system"]["esp_type"]);
|
parseDisplay(root["display"], root["system"]["esp_type"]);
|
||||||
|
getAjax("/api/inverter/list", parseIv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,10 @@ h2 {
|
||||||
top: 5px;
|
top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topnav .mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
svg.icon {
|
svg.icon {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -131,7 +135,7 @@ span.seperator {
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide {
|
.hide {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 992px) {
|
@media only screen and (min-width: 992px) {
|
||||||
|
@ -152,7 +156,7 @@ span.seperator {
|
||||||
padding-left: 24px !important;
|
padding-left: 24px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topnav .hide {
|
.topnav .mobile {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,10 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>System</title>
|
<title>System</title>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
{#HTML_HEADER}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<script type="text/javascript" src="api.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="topnav">
|
{#HTML_NAV}
|
||||||
<a href="/" class="title">AhoyDTU</a>
|
|
||||||
<a href="javascript:void(0);" class="icon" onclick="topnav()">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</a>
|
|
||||||
<div id="topnav" class="hide"></div>
|
|
||||||
<div id="wifiicon" class="info"></div>
|
|
||||||
</div>
|
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<pre id="stat"></pre>
|
<pre id="stat"></pre>
|
||||||
|
@ -26,25 +15,10 @@
|
||||||
<div id="html" class="mt-3 mb-3"></div>
|
<div id="html" class="mt-3 mb-3"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
{#HTML_FOOTER}
|
||||||
<div class="left">
|
|
||||||
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
|
||||||
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<ul>
|
|
||||||
<li><span id="version"></span></li>
|
|
||||||
<li><span id="esp_type"></span></li>
|
|
||||||
<li><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function parseGeneric(obj) {
|
function parseGeneric(obj) {
|
||||||
parseVersion(obj);
|
parseNav(obj);
|
||||||
parseESP(obj);
|
parseESP(obj);
|
||||||
parseRssi(obj);
|
parseRssi(obj);
|
||||||
}
|
}
|
||||||
|
@ -123,7 +97,6 @@
|
||||||
|
|
||||||
function parse(obj) {
|
function parse(obj) {
|
||||||
if(null != obj) {
|
if(null != obj) {
|
||||||
parseMenu(obj["menu"]);
|
|
||||||
parseGeneric(obj["generic"]);
|
parseGeneric(obj["generic"]);
|
||||||
|
|
||||||
if(null != obj["refresh"]) {
|
if(null != obj["refresh"]) {
|
||||||
|
|
|
@ -2,21 +2,10 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Update</title>
|
<title>Update</title>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
{#HTML_HEADER}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<script type="text/javascript" src="api.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="topnav">
|
{#HTML_NAV}
|
||||||
<a href="/" class="title">AhoyDTU</a>
|
|
||||||
<a href="javascript:void(0);" class="icon" onclick="topnav()">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</a>
|
|
||||||
<div id="topnav" class="hide"></div>
|
|
||||||
<div id="wifiicon" class="info"></div>
|
|
||||||
</div>
|
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<form id="form" method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8">
|
<form id="form" method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8">
|
||||||
|
@ -25,43 +14,21 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
{#HTML_FOOTER}
|
||||||
<div class="left">
|
|
||||||
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
|
||||||
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<ul>
|
|
||||||
<li><span id="version"></span></li>
|
|
||||||
<li><span id="esp_type"></span></li>
|
|
||||||
<li><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function parseGeneric(obj) {
|
function parseGeneric(obj) {
|
||||||
parseVersion(obj);
|
parseNav(obj);
|
||||||
parseESP(obj);
|
parseESP(obj);
|
||||||
parseRssi(obj);
|
parseRssi(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse(obj) {
|
|
||||||
if(null != obj) {
|
|
||||||
parseMenu(obj["menu"]);
|
|
||||||
parseGeneric(obj["generic"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
document.getElementById("form").submit();
|
document.getElementById("form").submit();
|
||||||
var e = document.getElementById("content");
|
var e = document.getElementById("content");
|
||||||
e.replaceChildren(span("update started"));
|
e.replaceChildren(span("update started"));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAjax("/api/index", parse);
|
getAjax("/api/generic", parseGeneric);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,50 +2,24 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Live</title>
|
<title>Live</title>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
{#HTML_HEADER}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<script type="text/javascript" src="api.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="topnav">
|
{#HTML_NAV}
|
||||||
<a href="/" class="title">AhoyDTU</a>
|
|
||||||
<a href="javascript:void(0);" class="icon" onclick="topnav()">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</a>
|
|
||||||
<div id="topnav" class="hide"></div>
|
|
||||||
<div id="wifiicon" class="info"></div>
|
|
||||||
</div>
|
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div id="live"></div>
|
<div id="live"></div>
|
||||||
<p>Every <span id="refresh"></span> seconds the values are updated</p>
|
<p>Every <span id="refresh"></span> seconds the values are updated</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
{#HTML_FOOTER}
|
||||||
<div class="left">
|
|
||||||
<a href="https://ahoydtu.de" target="_blank">AhoyDTU © 2023</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://discord.gg/WzhxEY62mB" target="_blank">Discord</a></li>
|
|
||||||
<li><a href="https://github.com/lumapu/ahoy" target="_blank">Github</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<ul>
|
|
||||||
<li><span id="version"></span></li>
|
|
||||||
<li><span id="esp_type"></span></li>
|
|
||||||
<li><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de" target="_blank" >CC BY-NC-SA 3.0</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var exeOnce = true;
|
var exeOnce = true;
|
||||||
|
|
||||||
function parseGeneric(obj) {
|
function parseGeneric(obj) {
|
||||||
if(true == exeOnce){
|
if(true == exeOnce){
|
||||||
parseVersion(obj);
|
parseNav(obj);
|
||||||
parseESP(obj);
|
parseESP(obj);
|
||||||
}
|
}
|
||||||
parseRssi(obj);
|
parseRssi(obj);
|
||||||
|
@ -133,8 +107,6 @@
|
||||||
|
|
||||||
function parse(obj) {
|
function parse(obj) {
|
||||||
if(null != obj) {
|
if(null != obj) {
|
||||||
if(true == exeOnce)
|
|
||||||
parseMenu(obj["menu"]);
|
|
||||||
parseGeneric(obj["generic"]);
|
parseGeneric(obj["generic"]);
|
||||||
parseIv(obj["inverter"], obj);
|
parseIv(obj["inverter"], obj);
|
||||||
document.getElementById("refresh").innerHTML = obj["refresh_interval"];
|
document.getElementById("refresh").innerHTML = obj["refresh_interval"];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue