mirror of
https://github.com/lumapu/ahoy.git
synced 2025-04-29 10:16:21 +02:00
Merge branch 'lumapu:development03' into development03
This commit is contained in:
commit
ce78534066
8 changed files with 101 additions and 116 deletions
|
@ -1,5 +1,9 @@
|
|||
# Development Changes
|
||||
|
||||
## 0.6.2 - 2023-04-04
|
||||
* fix login from multiple clients #819
|
||||
* fix login screen on small displays
|
||||
|
||||
## 0.6.1 - 2023-04-01
|
||||
* merge LED fix - LED1 shows MqTT state, LED configureable active high/low #839
|
||||
* only publish new inverter data #826
|
||||
|
|
|
@ -161,8 +161,8 @@ class app : public IApp, public ah::Scheduler {
|
|||
return mMqtt.getRxCnt();
|
||||
}
|
||||
|
||||
bool getProtection() {
|
||||
return mWeb.getProtection();
|
||||
bool getProtection(AsyncWebServerRequest *request) {
|
||||
return mWeb.isProtected(request);
|
||||
}
|
||||
|
||||
uint8_t getIrqPin(void) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "defines.h"
|
||||
#include "hm/hmSystem.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
// abstract interface to App. Make members of App accessible from child class
|
||||
// like web or API without forward declaration
|
||||
|
@ -47,7 +48,7 @@ class IApp {
|
|||
virtual uint32_t getMqttRxCnt() = 0;
|
||||
virtual uint32_t getMqttTxCnt() = 0;
|
||||
|
||||
virtual bool getProtection() = 0;
|
||||
virtual bool getProtection(AsyncWebServerRequest *request) = 0;
|
||||
};
|
||||
|
||||
#endif /*__IAPP_H__*/
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 6
|
||||
#define VERSION_PATCH 1
|
||||
#define VERSION_PATCH 2
|
||||
|
||||
//-------------------------------------
|
||||
typedef struct {
|
||||
|
|
|
@ -77,19 +77,19 @@ class RestApi {
|
|||
JsonObject root = response->getRoot();
|
||||
|
||||
String path = request->url().substring(5);
|
||||
if(path == "html/system") getHtmlSystem(root);
|
||||
else if(path == "html/logout") getHtmlLogout(root);
|
||||
else if(path == "html/reboot") getHtmlReboot(root);
|
||||
else if(path == "html/save") getHtmlSave(root);
|
||||
else if(path == "system") getSysInfo(root);
|
||||
else if(path == "generic") getGeneric(root);
|
||||
else if(path == "reboot") getReboot(root);
|
||||
if(path == "html/system") getHtmlSystem(request, root);
|
||||
else if(path == "html/logout") getHtmlLogout(request, root);
|
||||
else if(path == "html/reboot") getHtmlReboot(request, root);
|
||||
else if(path == "html/save") getHtmlSave(request, root);
|
||||
else if(path == "system") getSysInfo(request, root);
|
||||
else if(path == "generic") getGeneric(request, root);
|
||||
else if(path == "reboot") getReboot(request, root);
|
||||
else if(path == "statistics") getStatistics(root);
|
||||
else if(path == "inverter/list") getInverterList(root);
|
||||
else if(path == "index") getIndex(root);
|
||||
else if(path == "setup") getSetup(root);
|
||||
else if(path == "index") getIndex(request, root);
|
||||
else if(path == "setup") getSetup(request, root);
|
||||
else if(path == "setup/networks") getNetworks(root);
|
||||
else if(path == "live") getLive(root);
|
||||
else if(path == "live") getLive(request, root);
|
||||
else if(path == "record/info") getRecord(root, InverterDevInform_All);
|
||||
else if(path == "record/alarm") getRecord(root, AlarmData);
|
||||
else if(path == "record/config") getRecord(root, SystemConfigPara);
|
||||
|
@ -189,10 +189,10 @@ class RestApi {
|
|||
fp.close();
|
||||
}
|
||||
|
||||
void getGeneric(JsonObject obj) {
|
||||
void getGeneric(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
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_prot")] = mApp->getProtection(request);
|
||||
obj[F("menu_mask")] = (uint16_t)(mConfig->sys.protectionMask );
|
||||
obj[F("menu_protEn")] = (bool) (strlen(mConfig->sys.adminPwd) > 0);
|
||||
|
||||
|
@ -203,7 +203,7 @@ class RestApi {
|
|||
#endif
|
||||
}
|
||||
|
||||
void getSysInfo(JsonObject obj) {
|
||||
void getSysInfo(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
obj[F("ssid")] = mConfig->sys.stationSsid;
|
||||
obj[F("device_name")] = mConfig->sys.deviceName;
|
||||
obj[F("dark_mode")] = (bool)mConfig->sys.darkMode;
|
||||
|
@ -218,7 +218,7 @@ class RestApi {
|
|||
obj[F("heap_free")] = mHeapFree;
|
||||
obj[F("sketch_total")] = ESP.getFreeSketchSpace();
|
||||
obj[F("sketch_used")] = ESP.getSketchSize() / 1024; // in kb
|
||||
getGeneric(obj);
|
||||
getGeneric(request, obj);
|
||||
|
||||
getRadio(obj.createNestedObject(F("radio")));
|
||||
getStatistics(obj.createNestedObject(F("statistics")));
|
||||
|
@ -252,34 +252,34 @@ class RestApi {
|
|||
obj[F("schMax")] = max;
|
||||
}
|
||||
|
||||
void getHtmlSystem(JsonObject obj) {
|
||||
getSysInfo(obj.createNestedObject(F("system")));
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
void getHtmlSystem(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getSysInfo(request, obj.createNestedObject(F("system")));
|
||||
getGeneric(request, 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>");
|
||||
}
|
||||
|
||||
void getHtmlLogout(JsonObject obj) {
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
void getHtmlLogout(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
obj[F("refresh")] = 3;
|
||||
obj[F("refresh_url")] = "/";
|
||||
obj[F("html")] = F("succesfully logged out");
|
||||
}
|
||||
|
||||
void getHtmlReboot(JsonObject obj) {
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
void getHtmlReboot(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
obj[F("refresh")] = 20;
|
||||
obj[F("refresh_url")] = "/";
|
||||
obj[F("html")] = F("rebooting ...");
|
||||
}
|
||||
|
||||
void getHtmlSave(JsonObject obj) {
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
void getHtmlSave(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
obj["pending"] = (bool)mApp->getSavePending();
|
||||
obj["success"] = (bool)mApp->getLastSaveSucceed();
|
||||
}
|
||||
|
||||
void getReboot(JsonObject obj) {
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
void getReboot(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
obj[F("refresh")] = 10;
|
||||
obj[F("refresh_url")] = "/";
|
||||
obj[F("html")] = F("reboot. Autoreload after 10 seconds");
|
||||
|
@ -430,8 +430,8 @@ class RestApi {
|
|||
obj[F("disp_bsy")] = (mConfig->plugin.display.type < 10) ? DEF_PIN_OFF : mConfig->plugin.display.disp_busy;
|
||||
}
|
||||
|
||||
void getIndex(JsonObject obj) {
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
void getIndex(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
obj[F("ts_now")] = mApp->getTimestamp();
|
||||
obj[F("ts_sunrise")] = mApp->getSunrise();
|
||||
obj[F("ts_sunset")] = mApp->getSunset();
|
||||
|
@ -479,9 +479,9 @@ class RestApi {
|
|||
info.add(F("MQTT publishes in a fixed interval of ") + String(mConfig->mqtt.interval) + F(" seconds"));
|
||||
}
|
||||
|
||||
void getSetup(JsonObject obj) {
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
getSysInfo(obj.createNestedObject(F("system")));
|
||||
void getSetup(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
getSysInfo(request, obj.createNestedObject(F("system")));
|
||||
//getInverterList(obj.createNestedObject(F("inverter")));
|
||||
getMqtt(obj.createNestedObject(F("mqtt")));
|
||||
getNtp(obj.createNestedObject(F("ntp")));
|
||||
|
@ -497,8 +497,8 @@ class RestApi {
|
|||
mApp->getAvailNetworks(obj);
|
||||
}
|
||||
|
||||
void getLive(JsonObject obj) {
|
||||
getGeneric(obj.createNestedObject(F("generic")));
|
||||
void getLive(AsyncWebServerRequest *request, JsonObject obj) {
|
||||
getGeneric(request, obj.createNestedObject(F("generic")));
|
||||
obj[F("refresh")] = mConfig->nrf.sendInterval;
|
||||
|
||||
for (uint8_t fld = 0; fld < sizeof(acList); fld++) {
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
<form action="/login" method="post">
|
||||
<div class="row"><h2>AhoyDTU</h2></div>
|
||||
<div class="row">
|
||||
<div class="col-8"><input type="password" name="pwd" autofocus></div>
|
||||
<div class="col-4"><input type="submit" name="login" value="login" class="btn"></div>
|
||||
<div class="col-12 col-sm-8 mb-3"><input type="password" name="pwd" autofocus></div>
|
||||
<div class="col-12 col-sm-4"><input type="submit" name="login" value="login" class="btn"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -546,6 +546,18 @@ div.hr {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
#login {
|
||||
width: 450px;
|
||||
height: 200px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: var(--input-bg);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -160px;
|
||||
margin-left: -225px;
|
||||
}
|
||||
|
||||
@media(max-width: 500px) {
|
||||
div.ch .unit, div.ch-iv .unit {
|
||||
font-size: 18px;
|
||||
|
@ -559,6 +571,11 @@ div.hr {
|
|||
.subgrp {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
#login {
|
||||
margin-left: -150px;
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
#serial {
|
||||
|
@ -578,18 +595,6 @@ div.hr {
|
|||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#login {
|
||||
width: 450px;
|
||||
height: 200px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: var(--input-bg);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -160px;
|
||||
margin-left: -225px;
|
||||
}
|
||||
|
||||
|
||||
.head {
|
||||
background-color: var(--primary);
|
||||
|
|
107
src/web/web.h
107
src/web/web.h
|
@ -126,8 +126,19 @@ class Web {
|
|||
mProtected = protect;
|
||||
}
|
||||
|
||||
bool getProtection() {
|
||||
return mProtected;
|
||||
bool isProtected(AsyncWebServerRequest *request) {
|
||||
bool prot;
|
||||
prot = mProtected;
|
||||
if(!prot) {
|
||||
uint8_t ip[4];
|
||||
ah::ip2Arr(ip, request->client()->remoteIP().toString().c_str());
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
if(mLoginIp[i] != ip[i])
|
||||
prot = true;
|
||||
}
|
||||
}
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
void showUpdate2(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
|
@ -216,7 +227,7 @@ class Web {
|
|||
}
|
||||
|
||||
private:
|
||||
void checkRedirect(AsyncWebServerRequest *request) {
|
||||
inline void checkRedirect(AsyncWebServerRequest *request) {
|
||||
if ((mConfig->sys.protectionMask & PROT_MASK_INDEX) != PROT_MASK_INDEX)
|
||||
request->redirect(F("/index"));
|
||||
else if ((mConfig->sys.protectionMask & PROT_MASK_LIVE) != PROT_MASK_LIVE)
|
||||
|
@ -229,15 +240,18 @@ class Web {
|
|||
request->redirect(F("/login"));
|
||||
}
|
||||
|
||||
void checkProtection(AsyncWebServerRequest *request) {
|
||||
if(isProtected(request)) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void onUpdate(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onUpdate"));
|
||||
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_UPDATE)) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_UPDATE))
|
||||
checkProtection(request);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), update_html, update_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
|
@ -290,12 +304,8 @@ class Web {
|
|||
void onIndex(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onIndex"));
|
||||
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_INDEX)) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_INDEX))
|
||||
checkProtection(request);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), index_html, index_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
|
@ -308,6 +318,7 @@ class Web {
|
|||
if (request->args() > 0) {
|
||||
if (String(request->arg("pwd")) == String(mConfig->sys.adminPwd)) {
|
||||
mProtected = false;
|
||||
ah::ip2Arr(mLoginIp, request->client()->remoteIP().toString().c_str());
|
||||
request->redirect("/");
|
||||
}
|
||||
}
|
||||
|
@ -320,10 +331,7 @@ class Web {
|
|||
void onLogout(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onLogout"));
|
||||
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
checkProtection(request);
|
||||
|
||||
mProtected = true;
|
||||
|
||||
|
@ -367,10 +375,8 @@ class Web {
|
|||
}
|
||||
|
||||
void showNotFound(AsyncWebServerRequest *request) {
|
||||
if (mProtected)
|
||||
checkRedirect(request);
|
||||
else
|
||||
request->redirect("/setup");
|
||||
checkProtection(request);
|
||||
request->redirect("/setup");
|
||||
}
|
||||
|
||||
void onReboot(AsyncWebServerRequest *request) {
|
||||
|
@ -381,10 +387,7 @@ class Web {
|
|||
}
|
||||
|
||||
void showErase(AsyncWebServerRequest *request) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
checkProtection(request);
|
||||
|
||||
DPRINTLN(DBG_VERBOSE, F("showErase"));
|
||||
mApp->eraseSettings(false);
|
||||
|
@ -392,10 +395,7 @@ class Web {
|
|||
}
|
||||
|
||||
void showFactoryRst(AsyncWebServerRequest *request) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
checkProtection(request);
|
||||
|
||||
DPRINTLN(DBG_VERBOSE, F("showFactoryRst"));
|
||||
String content = "";
|
||||
|
@ -424,12 +424,8 @@ class Web {
|
|||
void onSetup(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onSetup"));
|
||||
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SETUP)) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SETUP))
|
||||
checkProtection(request);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), setup_html, setup_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
|
@ -439,10 +435,7 @@ class Web {
|
|||
void showSave(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("showSave"));
|
||||
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
checkProtection(request);
|
||||
|
||||
if (request->args() == 0)
|
||||
return;
|
||||
|
@ -605,12 +598,8 @@ class Web {
|
|||
void onLive(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onLive"));
|
||||
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_LIVE)) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_LIVE))
|
||||
checkProtection(request);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), visualization_html, visualization_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
|
@ -620,13 +609,6 @@ class Web {
|
|||
}
|
||||
|
||||
void onAbout(AsyncWebServerRequest *request) {
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_LIVE)) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), about_html, about_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
response->addHeader(F("content-type"), "text/html; charset=UTF-8");
|
||||
|
@ -643,12 +625,8 @@ class Web {
|
|||
void onSerial(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onSerial"));
|
||||
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SERIAL)) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SERIAL))
|
||||
checkProtection(request);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), serial_html, serial_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
|
@ -658,12 +636,8 @@ class Web {
|
|||
void onSystem(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onSystem"));
|
||||
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SYSTEM)) {
|
||||
if (mProtected) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, PROT_MASK_SYSTEM))
|
||||
checkProtection(request);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
|
@ -840,6 +814,7 @@ class Web {
|
|||
AsyncEventSource mEvts;
|
||||
bool mProtected;
|
||||
uint32_t mLogoutTimeout;
|
||||
uint8_t mLoginIp[4];
|
||||
IApp *mApp;
|
||||
HMSYSTEM *mSys;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue