Browse Source

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
pull/729/head
lumapu 2 years ago
parent
commit
4f0d365211
  1. 1
      .gitignore
  2. 6
      src/CHANGES.md
  3. 15
      src/app.cpp
  4. 2
      src/config/settings.h
  5. 2
      src/defines.h
  6. 3
      src/hm/hmPayload.h
  7. 33
      src/publisher/pubMqtt.h
  8. 61
      src/web/RestApi.h
  9. 25
      src/web/html/api.js
  10. 104
      src/web/html/convert.py
  11. 16
      src/web/html/includes/footer.html
  12. 3
      src/web/html/includes/header.html
  13. 27
      src/web/html/includes/nav.html
  14. 61
      src/web/html/index.html
  15. 25
      src/web/html/login.html
  16. 35
      src/web/html/serial.html
  17. 39
      src/web/html/setup.html
  18. 8
      src/web/html/style.css
  19. 35
      src/web/html/system.html
  20. 43
      src/web/html/update.html
  21. 36
      src/web/html/visualization.html

1
.gitignore

@ -6,6 +6,7 @@
.vscode/extensions.json
src/config/config_override.h
src/web/html/h/*
src/web/html/tmp/*
/**/Debug
/**/v16/*
*.db

6
src/CHANGES.md

@ -2,6 +2,12 @@
(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
* merged PR #684, #698, #705
* webserial minor overflow fix #660

15
src/app.cpp

@ -21,12 +21,6 @@ void app::setup() {
resetSystem();
/*DBGPRINTLN("--- start");
DBGPRINTLN(String(ESP.getFreeHeap()));
DBGPRINTLN(String(ESP.getHeapFragmentation()));
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
mSettings.setup();
mSettings.getPtr(mConfig);
DPRINT(DBG_INFO, F("Settings valid: "));
@ -50,6 +44,7 @@ void app::setup() {
#endif
mSys.addInverters(&mConfig->inst);
mPayload.setup(this, &mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
mPayload.enableSerialDebug(mConfig->serial.debug);
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.enableSerialDebug(mConfig->serial.debug);
/*DBGPRINTLN("--- after payload");
DBGPRINTLN("--- after payload");
DBGPRINTLN(String(ESP.getFreeHeap()));
DBGPRINTLN(String(ESP.getHeapFragmentation()));
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));
if(!mSys.Radio.isChipConnected())
DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring"));
@ -90,10 +85,10 @@ void app::setup() {
regularTickers();
/*DBGPRINTLN("--- end setup");
DBGPRINTLN("--- end setup");
DBGPRINTLN(String(ESP.getFreeHeap()));
DBGPRINTLN(String(ESP.getHeapFragmentation()));
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));
}
//-----------------------------------------------------------------------------

2
src/config/settings.h

@ -250,7 +250,7 @@ class settings {
return false;
}
DynamicJsonDocument json(4500);
DynamicJsonDocument json(5500);
JsonObject root = json.to<JsonObject>();
jsonWifi(root.createNestedObject(F("wifi")), true);
jsonNrf(root.createNestedObject(F("nrf")), true);

2
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 5
#define VERSION_PATCH 90
#define VERSION_PATCH 91
//-------------------------------------
typedef struct {

3
src/hm/hmPayload.h

@ -157,7 +157,8 @@ class HmPayload {
uint8_t cmd = iv->getQueuedCmd();
DPRINT(DBG_INFO, F("(#"));
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);
mPayload[iv->id].txCmd = cmd;
}

33
src/publisher/pubMqtt.h

@ -419,17 +419,19 @@ class PubMqtt {
iv = mSys->getInverterByPos(id);
if (NULL == iv)
continue; // skip to next inverter
if (!iv->config->enabled)
continue; // skip to next inverter
rec = iv->getRecordStruct(RealTimeRunData_Debug);
// inverter status
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;
}
else // inverter is enabled but not available
allAvail = false;
}
if(mLastIvState[id] != status) {
// if status changed from producing to not producing send last data immediately
@ -439,11 +441,11 @@ class PubMqtt {
mLastIvState[id] = status;
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);
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));
publish(topic, val, true);
}
@ -451,7 +453,7 @@ class PubMqtt {
if(changed) {
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;
@ -474,6 +476,7 @@ class PubMqtt {
char topic[7 + MQTT_TOPIC_LEN], val[40];
record_t<> *rec = iv->getRecordStruct(curInfoCmd);
if (iv->getLastTs(rec) > 0) {
for (uint8_t i = 0; i < rec->length; i++) {
bool retained = false;
if (curInfoCmd == RealTimeRunData_Debug) {
@ -494,6 +497,7 @@ class PubMqtt {
yield();
}
}
}
void sendIvData() {
bool anyAvail = processIvStatus();
@ -512,19 +516,25 @@ class PubMqtt {
uint8_t curInfoCmd = mSendList.front();
if ((curInfoCmd != RealTimeRunData_Debug) || !RTRDataHasBeenSent) { // send RTR Data only once
bool sendTotals = (curInfoCmd == RealTimeRunData_Debug);
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id);
if (NULL == iv)
continue; // skip to next inverter
if (!iv->config->enabled)
continue; // skip to next inverter
// 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);
// calculate total values for RealTimeRunData_Debug
if (curInfoCmd == RealTimeRunData_Debug) {
if (sendTotals) {
record_t<> *rec = iv->getRecordStruct(curInfoCmd);
sendTotals &= (iv->getLastTs(rec) > 0);
if (sendTotals) {
for (uint8_t i = 0; i < rec->length; i++) {
if (CH0 == rec->assign[i].ch) {
switch (rec->assign[i].fieldId) {
@ -543,11 +553,12 @@ class PubMqtt {
}
}
}
yield();
}
}
yield();
}
if (curInfoCmd == RealTimeRunData_Debug) {
if (sendTotals) {
uint8_t fieldId;
for (uint8_t i = 0; i < 4; i++) {
switch (i) {
@ -565,7 +576,7 @@ class PubMqtt {
fieldId = FLD_PDC;
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]));
publish(topic, val, true);
}

61
src/web/RestApi.h

@ -71,7 +71,7 @@ class RestApi {
mHeapFrag = ESP.getHeapFragmentation();
#endif
AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192);
AsyncJsonResponse* response = new AsyncJsonResponse(false, 6000);
JsonObject root = response->getRoot();
String path = request->url().substring(5);
@ -83,7 +83,6 @@ class RestApi {
else if(path == "reboot") getReboot(root);
else if(path == "statistics") getStatistics(root);
else if(path == "inverter/list") getInverterList(root);
else if(path == "menu") getMenu(root);
else if(path == "index") getIndex(root);
else if(path == "setup") getSetup(root);
else if(path == "setup/networks") getNetworks(root);
@ -183,10 +182,13 @@ class RestApi {
}
void getGeneric(JsonObject obj) {
obj[F("version")] = String(mApp->getVersion());
obj[F("build")] = String(AUTO_GIT_HASH);
obj[F("wifi_rssi")] = (WiFi.status() != WL_CONNECTED) ? 0 : WiFi.RSSI();
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)
obj[F("esp_type")] = F("ESP32");
@ -244,7 +246,6 @@ class RestApi {
}
void getHtmlSystem(JsonObject obj) {
getMenu(obj.createNestedObject(F("menu")));
getSysInfo(obj.createNestedObject(F("system")));
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>");
@ -252,7 +253,6 @@ class RestApi {
}
void getHtmlLogout(JsonObject obj) {
getMenu(obj.createNestedObject(F("menu")));
getGeneric(obj.createNestedObject(F("generic")));
obj[F("refresh")] = 3;
obj[F("refresh_url")] = "/";
@ -260,7 +260,6 @@ class RestApi {
}
void getHtmlSave(JsonObject obj) {
getMenu(obj.createNestedObject(F("menu")));
getGeneric(obj.createNestedObject(F("generic")));
obj[F("refresh")] = 2;
obj[F("refresh_url")] = "/setup";
@ -268,7 +267,6 @@ class RestApi {
}
void getReboot(JsonObject obj) {
getMenu(obj.createNestedObject(F("menu")));
getGeneric(obj.createNestedObject(F("generic")));
obj[F("refresh")] = 10;
obj[F("refresh_url")] = "/";
@ -377,54 +375,9 @@ class RestApi {
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) {
getMenu(obj.createNestedObject(F("menu")));
getGeneric(obj.createNestedObject(F("generic")));
obj[F("ts_now")] = mApp->getTimestamp();
obj[F("ts_sunrise")] = mApp->getSunrise();
obj[F("ts_sunset")] = mApp->getSunset();
@ -473,10 +426,9 @@ class RestApi {
}
void getSetup(JsonObject obj) {
getMenu(obj.createNestedObject(F("menu")));
getGeneric(obj.createNestedObject(F("generic")));
getSysInfo(obj.createNestedObject(F("system")));
getInverterList(obj.createNestedObject(F("inverter")));
//getInverterList(obj.createNestedObject(F("inverter")));
getMqtt(obj.createNestedObject(F("mqtt")));
getNtp(obj.createNestedObject(F("ntp")));
getSun(obj.createNestedObject(F("sun")));
@ -492,7 +444,6 @@ class RestApi {
}
void getLive(JsonObject obj) {
getMenu(obj.createNestedObject(F("menu")));
getGeneric(obj.createNestedObject(F("generic")));
JsonArray invArr = obj.createNestedArray(F("inverter"));
obj["refresh_interval"] = mConfig->nrf.sendInterval;

25
src/web/html/api.js

@ -38,18 +38,21 @@ function topnav() {
toggle("topnav");
}
function parseMenu(obj) {
var e = document.getElementById("topnav");
e.innerHTML = "";
for(var i = 0; i < obj["name"].length; i ++) {
if(obj["name"][i] == "-")
e.appendChild(span("", ["seperator"]));
else {
var l = link(obj["link"][i], obj["name"][i], obj["trgt"][i]);
if(obj["link"][i] == window.location.pathname)
function parseNav(obj) {
for(i = 0; i < 7; i++) {
var l = document.getElementById("nav"+i);
if(window.location.pathname == "/" + l.href.split('/').pop())
l.classList.add("active");
e.appendChild(l);
}
if(obj["menu_protEn"]) {
if(obj["menu_prot"]) {
if((((obj["menu_mask"] >> i) & 0x01) == 0x01) || (1 == i))
l.classList.remove("hide");
} else if(0 == i)
l.classList.remove("hide");
} else if(i > 1)
l.classList.remove("hide");
}
}

104
src/web/html/convert.py

@ -2,10 +2,82 @@ import re
import os
import gzip
import glob
import shutil
import pkg_resources
from datetime import date
from pathlib import Path
from dulwich import porcelain
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()
def convert2Header(inFile):
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]
define = inFile.split(".")[0].upper()
define2 = inFile.split(".")[1].upper()
@ -17,10 +89,15 @@ def convert2Header(inFile):
Path("html/h").mkdir(exist_ok=True)
else:
outName = "h/" + inFileVarName + ".h"
Path("h").mkdir(exist_ok=True)
data = ""
if fileType == "ico":
f = open(inFile, "rb")
data = f.read()
f.close()
else:
if fileType == "html":
data = htmlParts(inFile, "includes/header.html", "includes/nav.html", "includes/footer.html", version)
else:
f = open(inFile, "r")
data = f.read()
@ -53,13 +130,17 @@ def convert2Header(inFile):
f.close()
# delete all files in the 'h' dir
dir = 'h'
wd = 'h'
if os.getcwd()[-4:] != "html":
dir = "web/html/" + dir
wd = "web/html/" + wd
if os.path.exists(dir):
for f in os.listdir(dir):
os.remove(os.path.join(dir, f))
if os.path.exists(wd):
for f in os.listdir(wd):
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
if os.getcwd()[-4:] != "html":
@ -69,6 +150,11 @@ files_grabbed = []
for files in types:
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
for val in files_grabbed:
convert2Header(val)
convert2Header(val, version)

16
src/web/html/includes/footer.html

@ -0,0 +1,16 @@
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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

@ -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

@ -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>

61
src/web/html/index.html

@ -2,36 +2,12 @@
<html>
<head>
<title>Index</title>
<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>
{#HTML_HEADER}
</head>
<body>
<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="hide"></div>
<div id="wifiicon" class="info"></div>
</div>
{#HTML_NAV}
<div id="wrapper">
<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>
<span class="des">Uptime: </span><span id="uptime"></span><br/>
<span class="des">ESP-Time: </span><span id="date"></span>
@ -60,22 +36,7 @@
</div>
</div>
</div>
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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>
{#HTML_FOOTER}
<script type="text/javascript">
var exeOnce = true;
var tickCnt = 0;
@ -108,10 +69,10 @@
function parseGeneric(obj) {
// Disclaimer
//if(obj["disclaimer"] == false) sessionStorage.setItem("gDisclaimer", promptFunction());
if(exeOnce){
/*if(exeOnce){
parseVersion(obj);
parseESP(obj);
}
}*/
parseRssi(obj);
}
@ -203,7 +164,7 @@
document.getElementById("iv").replaceChildren(p);
}
function parseWarnInfo(warn, success, version) {
function parseWarnInfo(warn, success) {
var p = div(["none"]);
for(var w of warn) {
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());
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());
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());
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());
else
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) {
if(null != obj) {
if(exeOnce)
parseMenu(obj["menu"]);
parseNav(obj["generic"]);
parseGeneric(obj["generic"]);
parseSys(obj);
parseIv(obj["inverter"]);
parseWarnInfo(obj["warnings"], obj["infos"], obj["generic"]["version"]);
parseWarnInfo(obj["warnings"], obj["infos"]);
if(exeOnce) {
window.setInterval("tick()", 1000);
exeOnce = false;

25
src/web/html/login.html

@ -2,9 +2,7 @@
<html>
<head>
<title>Login</title>
<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>
{#HTML_HEADER}
</head>
<body>
<div id="wrapper">
@ -18,25 +16,6 @@
</div>
</div>
</div>
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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>
{#HTML_FOOTER}
</body>
</html>

35
src/web/html/serial.html

@ -2,21 +2,10 @@
<html>
<head>
<title>Serial Console</title>
<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>
{#HTML_HEADER}
</head>
<body>
<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="hide"></div>
<div id="wifiicon" class="info"></div>
</div>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<div class="serial">
@ -53,22 +42,7 @@
</div>
</div>
</div>
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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>
{#HTML_FOOTER}
<script type="text/javascript">
var mAutoScroll = true;
var con = document.getElementById("serial");
@ -87,7 +61,7 @@
parseRssi(obj);
if(true == exeOnce) {
parseVersion(obj);
parseNav(obj);
parseESP(obj);
window.setInterval("getAjax('/api/generic', parseGeneric)", 10000);
exeOnce = false;
@ -96,7 +70,6 @@
}
function parse(root) {
parseMenu(root["menu"]);
select = document.getElementById('InvID');
if(null == root) return;

39
src/web/html/setup.html

@ -2,9 +2,7 @@
<html>
<head>
<title>Setup</title>
<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>
{#HTML_HEADER}
<script type="text/javascript">
function load() {
for(it of document.getElementsByClassName("s_collapsible")) {
@ -18,16 +16,7 @@
</script>
</head>
<body onload="load()">
<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="hide"></div>
<div id="wifiicon" class="info"></div>
</div>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<form method="post" action="/save">
@ -224,22 +213,7 @@
</form>
</div>
</div>
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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>
{#HTML_FOOTER}
<script type="text/javascript">
var highestId = 0;
var maxInv = 0;
@ -426,7 +400,7 @@
iv.append(
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 [
@ -479,7 +453,7 @@
}
function parseGeneric(obj) {
parseVersion(obj);
parseNav(obj);
parseESP(obj);
parseRssi(obj);
}
@ -568,11 +542,9 @@
function parse(root) {
if(null != root) {
parseMenu(root["menu"]);
parseSys(root["system"]);
parseGeneric(root["generic"]);
parseStaticIp(root["static_ip"]);
parseIv(root["inverter"]);
parseMqtt(root["mqtt"]);
parseNtp(root["ntp"]);
parseSun(root["sun"]);
@ -580,6 +552,7 @@
parseRadio(root["radio"]);
parseSerial(root["serial"]);
parseDisplay(root["display"], root["system"]["esp_type"]);
getAjax("/api/inverter/list", parseIv);
}
}

8
src/web/html/style.css

@ -50,6 +50,10 @@ h2 {
top: 5px;
}
.topnav .mobile {
display: none;
}
svg.icon {
vertical-align: middle;
display: inline-block;
@ -131,7 +135,7 @@ span.seperator {
}
.hide {
display: none;
display: none !important;
}
@media only screen and (min-width: 992px) {
@ -152,7 +156,7 @@ span.seperator {
padding-left: 24px !important;
}
.topnav .hide {
.topnav .mobile {
display: block;
}

35
src/web/html/system.html

@ -2,21 +2,10 @@
<html>
<head>
<title>System</title>
<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>
{#HTML_HEADER}
</head>
<body>
<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="hide"></div>
<div id="wifiicon" class="info"></div>
</div>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<pre id="stat"></pre>
@ -26,25 +15,10 @@
<div id="html" class="mt-3 mb-3"></div>
</div>
</div>
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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>
{#HTML_FOOTER}
<script type="text/javascript">
function parseGeneric(obj) {
parseVersion(obj);
parseNav(obj);
parseESP(obj);
parseRssi(obj);
}
@ -123,7 +97,6 @@
function parse(obj) {
if(null != obj) {
parseMenu(obj["menu"]);
parseGeneric(obj["generic"]);
if(null != obj["refresh"]) {

43
src/web/html/update.html

@ -2,21 +2,10 @@
<html>
<head>
<title>Update</title>
<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>
{#HTML_HEADER}
</head>
<body>
<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="hide"></div>
<div id="wifiicon" class="info"></div>
</div>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<form id="form" method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8">
@ -25,43 +14,21 @@
</form>
</div>
</div>
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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>
{#HTML_FOOTER}
<script type="text/javascript">
function parseGeneric(obj) {
parseVersion(obj);
parseNav(obj);
parseESP(obj);
parseRssi(obj);
}
function parse(obj) {
if(null != obj) {
parseMenu(obj["menu"]);
parseGeneric(obj["generic"]);
}
}
function hide() {
document.getElementById("form").submit();
var e = document.getElementById("content");
e.replaceChildren(span("update started"));
}
getAjax("/api/index", parse);
getAjax("/api/generic", parseGeneric);
</script>
</body>
</html>

36
src/web/html/visualization.html

@ -2,50 +2,24 @@
<html>
<head>
<title>Live</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
{#HTML_HEADER}
<meta name="apple-mobile-web-app-capable" content="yes">
<script type="text/javascript" src="api.js"></script>
</head>
<body>
<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="hide"></div>
<div id="wifiicon" class="info"></div>
</div>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<div id="live"></div>
<p>Every <span id="refresh"></span> seconds the values are updated</p>
</div>
</div>
<div id="footer">
<div class="left">
<a href="https://ahoydtu.de" target="_blank">AhoyDTU &copy 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>
{#HTML_FOOTER}
<script type="text/javascript">
var exeOnce = true;
function parseGeneric(obj) {
if(true == exeOnce){
parseVersion(obj);
parseNav(obj);
parseESP(obj);
}
parseRssi(obj);
@ -133,8 +107,6 @@
function parse(obj) {
if(null != obj) {
if(true == exeOnce)
parseMenu(obj["menu"]);
parseGeneric(obj["generic"]);
parseIv(obj["inverter"], obj);
document.getElementById("refresh").innerHTML = obj["refresh_interval"];

Loading…
Cancel
Save