diff --git a/src/CHANGES.md b/src/CHANGES.md index 29c3d999..414da944 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,11 @@ # Changelog +## 0.5.57 +* improved stability +* added icons to index.html, added wifi-strength symbol on each page +* moved packet stats and sun to system.html +* refactored communication offset (adjustable in minutes now) + ## 0.5.56 * factory reset formats entire little fs * renamed sunrise / sunset on indext.html to start / stop communication diff --git a/src/app.cpp b/src/app.cpp index a2f7f93f..ed031d19 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -37,7 +37,7 @@ void app::setup() { #if !defined(AP_ONLY) - mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, mSys, &mTimestamp, &mSunrise, &mSunset); + mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, mSys, &mTimestamp); #endif mWifi.setup(mConfig, &mTimestamp); @@ -126,7 +126,9 @@ void app::loop(void) { mPayload.process(true, mConfig->nrf.maxRetransPerPyld, &mStat); } +#if !defined(AP_ONLY) mMqtt.loop(); +#endif } //----------------------------------------------------------------------------- @@ -149,9 +151,9 @@ void app::tickCalcSunrise(void) { uint32_t nxtTrig = mTimestamp - ((mTimestamp - 10) % 86400) + 86400; // next midnight, -10 for safety that it is certain next day onceAt(std::bind(&app::tickCalcSunrise, this), nxtTrig); if (mConfig->mqtt.broker[0] > 0) { - once(std::bind(&PubMqttType::tickerSun, &mMqtt), 1); - onceAt(std::bind(&PubMqttType::tickSunrise, &mMqtt), mSunrise); - onceAt(std::bind(&PubMqttType::tickSunset, &mMqtt), mSunset); + mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSec); + onceAt(std::bind(&PubMqttType::tickSunrise, &mMqtt), (mSunrise - mConfig->sun.offsetSec)); + onceAt(std::bind(&PubMqttType::tickSunset, &mMqtt), (mSunset + mConfig->sun.offsetSec)); } } @@ -161,7 +163,7 @@ void app::tickSend(void) { DPRINTLN(DBG_WARN, "NRF24 not connected!"); return; } - if ((mTimestamp > 0) && (!mConfig->sun.disNightCom || (mTimestamp >= mSunrise && mTimestamp <= mSunset))) { // Timestamp is set and (inverter communication only during the day if the option is activated and sunrise/sunset is set) + if ((mTimestamp > 0) && (!mConfig->sun.disNightCom || (mTimestamp >= (mSunrise - mConfig->sun.offsetSec) && mTimestamp <= (mSunset + mConfig->sun.offsetSec)))) { // Timestamp is set and (inverter communication only during the day if the option is activated and sunrise/sunset is set) if (!mSys->BufCtrl.empty()) { if (mConfig->serial.debug) DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys->BufCtrl.getFill())); diff --git a/src/app.h b/src/app.h index b7848224..c2d8a4e8 100644 --- a/src/app.h +++ b/src/app.h @@ -74,6 +74,7 @@ class app : public IApp, public ah::Scheduler { } bool saveSettings() { + mShowRebootRequest = true; return mSettings.saveSettings(); } diff --git a/src/config/settings.h b/src/config/settings.h index e1cd449f..5eb317ad 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -55,6 +55,7 @@ typedef struct { float lat; float lon; bool disNightCom; // disable night communication + uint16_t offsetSec; } cfgSun_t; typedef struct { @@ -173,7 +174,7 @@ class settings { //fp.seek(0, SeekSet); DynamicJsonDocument root(4096); DeserializationError err = deserializeJson(root, fp); - if(!err) { + if(!err && (root.size() > 0)) { mCfg.valid = true; jsonWifi(root["wifi"]); jsonNrf(root["nrf"]); @@ -262,6 +263,7 @@ class settings { mCfg.sun.lat = 0.0; mCfg.sun.lon = 0.0; mCfg.sun.disNightCom = false; + mCfg.sun.offsetSec = 0; mCfg.serial.interval = SERIAL_INTERVAL; mCfg.serial.showIv = false; @@ -334,13 +336,15 @@ class settings { void jsonSun(JsonObject obj, bool set = false) { if(set) { - obj[F("lat")] = mCfg.sun.lat; - obj[F("lon")] = mCfg.sun.lon; - obj[F("dis")] = mCfg.sun.disNightCom; + obj[F("lat")] = mCfg.sun.lat; + obj[F("lon")] = mCfg.sun.lon; + obj[F("dis")] = mCfg.sun.disNightCom; + obj[F("offs")] = mCfg.sun.offsetSec; } else { mCfg.sun.lat = obj[F("lat")]; mCfg.sun.lon = obj[F("lon")]; mCfg.sun.disNightCom = obj[F("dis")]; + mCfg.sun.offsetSec = obj[F("offs")]; } } diff --git a/src/defines.h b/src/defines.h index eca08e8e..56582290 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 5 -#define VERSION_PATCH 56 +#define VERSION_PATCH 57 //------------------------------------- typedef struct { diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index a28a6f04..e90aa095 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -297,6 +297,8 @@ class HmRadio { } uint8_t getDataRate(void) { + if(!mNrf24.isChipConnected()) + return 3; // unkown return mNrf24.getDataRate(); } diff --git a/src/hm/hmSystem.h b/src/hm/hmSystem.h index 5338d1a0..dad8b4aa 100644 --- a/src/hm/hmSystem.h +++ b/src/hm/hmSystem.h @@ -104,8 +104,14 @@ class HmSystem { } uint8_t getNumInverters(void) { - DPRINTLN(DBG_VERBOSE, F("hmSystem.h:getNumInverters")); - return mNumInv; + uint8_t num = 0; + INVERTERTYPE *p; + for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { + p = &mInverter[i]; + if(p->config->serial.u64 != 0ULL) + num++; + } + return num; } void enableDebug() { diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index cf98eec2..f67c1c00 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -40,14 +40,12 @@ class PubMqtt { ~PubMqtt() { } - void setup(cfgMqtt_t *cfg_mqtt, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs, uint32_t *sunrise, uint32_t *sunset) { + void setup(cfgMqtt_t *cfg_mqtt, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs) { mCfgMqtt = cfg_mqtt; mDevName = devName; mVersion = version; mSys = sys; mUtcTimestamp = utcTs; - mSunrise = sunrise; - mSunset = sunset; snprintf(mLwtTopic, MQTT_TOPIC_LEN + 5, "%s/mqtt", mCfgMqtt->topic); @@ -93,9 +91,11 @@ class PubMqtt { tickSunset(); } - void tickerSun() { - publish("sunrise", String(*mSunrise).c_str(), true); - publish("sunset", String(*mSunset).c_str(), true); + void tickerSun(uint32_t sunrise, uint32_t sunset, uint32_t offs) { + publish("sunrise", String(sunrise).c_str(), true); + publish("sunset", String(sunset).c_str(), true); + publish("comm_start", String(sunrise - offs).c_str(), true); + publish("comm_stop", String(sunset + offs).c_str(), true); } void tickSunrise() { @@ -505,7 +505,6 @@ class PubMqtt { WiFiEventHandler mHWifiCon, mHWifiDiscon; #endif - uint32_t *mSunrise, *mSunset; HMSYSTEM *mSys; uint32_t *mUtcTimestamp; uint32_t mRxCnt, mTxCnt; diff --git a/src/utils/sun.h b/src/utils/sun.h index 03a91f70..c66149c2 100644 --- a/src/utils/sun.h +++ b/src/utils/sun.h @@ -25,7 +25,7 @@ namespace ah { // Declination of the sun double delta = ASIN(SIN(lambda) * SIN(23.44)); // Hour angle - double omega = ACOS((SIN(-3.5) - SIN(lat) * SIN(delta)) / (COS(lat) * COS(delta))); //(SIN(-0.83) + double omega = ACOS((SIN(-0.83) - SIN(lat) * SIN(delta)) / (COS(lat) * COS(delta))); // Calculate sunrise and sunset double Jrise = Jtransit - omega / 360; double Jset = Jtransit + omega / 360; diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 831f0f93..eb262aae 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -60,6 +60,7 @@ class RestApi { else if(path == "html/logout") getHtmlLogout(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); else if(path == "statistics") getStatistics(root); else if(path == "inverter/list") getInverterList(root); @@ -144,17 +145,23 @@ class RestApi { request->send(response); } + 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(); + + #if defined(ESP32) + obj[F("esp_type")] = F("ESP32"); + #else + obj[F("esp_type")] = F("ESP8266"); + #endif + } + void getSysInfo(JsonObject obj) { obj[F("ssid")] = mConfig->sys.stationSsid; obj[F("device_name")] = mConfig->sys.deviceName; - obj[F("version")] = String(mApp->getVersion()); - obj[F("build")] = String(AUTO_GIT_HASH); - obj[F("ts_uptime")] = mApp->getUptime(); - obj[F("ts_now")] = mApp->getTimestamp(); - obj[F("ts_sunrise")] = mApp->getSunrise(); - obj[F("ts_sunset")] = mApp->getSunset(); - obj[F("wifi_rssi")] = WiFi.RSSI(); obj[F("mac")] = WiFi.macAddress(); obj[F("hostname")] = WiFi.getHostname(); obj[F("pwd_set")] = (strlen(mConfig->sys.adminPwd) > 0); @@ -164,9 +171,10 @@ class RestApi { obj[F("heap_free")] = ESP.getFreeHeap(); obj[F("sketch_total")] = ESP.getFreeSketchSpace(); obj[F("sketch_used")] = ESP.getSketchSize() / 1024; // in kb - + getGeneric(obj); getRadio(obj.createNestedObject(F("radio"))); + getStatistics(obj.createNestedObject(F("statistics"))); #if defined(ESP32) obj[F("heap_total")] = ESP.getHeapSize(); @@ -191,24 +199,19 @@ class RestApi { #endif //obj[F("littlefs_total")] = LittleFS.totalBytes(); //obj[F("littlefs_used")] = LittleFS.usedBytes(); - - #if defined(ESP32) - obj[F("esp_type")] = F("ESP32"); - #else - obj[F("esp_type")] = F("ESP8266"); - #endif } void getHtmlSystem(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); getSysInfo(obj.createNestedObject(F("system"))); + getGeneric(obj.createNestedObject(F("generic"))); obj[F("html")] = F("Factory Reset

Reboot"); } void getHtmlLogout(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); - getSysInfo(obj.createNestedObject(F("system"))); + getGeneric(obj.createNestedObject(F("generic"))); obj[F("refresh")] = 3; obj[F("refresh_url")] = "/"; obj[F("html")] = F("succesfully logged out"); @@ -216,7 +219,7 @@ class RestApi { void getHtmlSave(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); - getSysInfo(obj.createNestedObject(F("system"))); + getGeneric(obj.createNestedObject(F("generic"))); obj[F("refresh")] = 2; obj[F("refresh_url")] = "/setup"; obj[F("html")] = F("settings succesfully save"); @@ -224,7 +227,7 @@ class RestApi { void getReboot(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); - getSysInfo(obj.createNestedObject(F("system"))); + getGeneric(obj.createNestedObject(F("generic"))); obj[F("refresh")] = 10; obj[F("refresh_url")] = "/"; obj[F("html")] = F("reboot. Autoreload after 10 seconds"); @@ -282,6 +285,7 @@ class RestApi { obj[F("lat")] = mConfig->sun.lat ? String(mConfig->sun.lat, 5) : ""; obj[F("lon")] = mConfig->sun.lat ? String(mConfig->sun.lon, 5) : ""; obj[F("disnightcom")] = mConfig->sun.disNightCom; + obj[F("offs")] = mConfig->sun.offsetSec; } void getPinout(JsonObject obj) { @@ -343,10 +347,12 @@ class RestApi { void getIndex(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); - getSysInfo(obj.createNestedObject(F("system"))); - getRadio(obj.createNestedObject(F("radio"))); - getStatistics(obj.createNestedObject(F("statistics"))); - obj["refresh_interval"] = mConfig->nrf.sendInterval; + getGeneric(obj.createNestedObject(F("generic"))); + + obj[F("ts_now")] = mApp->getTimestamp(); + obj[F("ts_sunrise")] = mApp->getSunrise(); + obj[F("ts_sunset")] = mApp->getSunset(); + obj[F("ts_offset")] = mConfig->sun.offsetSec; JsonArray inv = obj.createNestedArray(F("inverter")); Inverter<> *iv; @@ -369,22 +375,27 @@ class RestApi { if(!mSys->Radio.isChipConnected()) warn.add(F("your NRF24 module can't be reached, check the wiring and pinout")); else if(!mSys->Radio.isPVariant()) - warn.add(F("your NRF24 module isn't a plus version(+), maybe incompatible!")); + warn.add(F("your NRF24 module isn't a plus version(+), maybe incompatible")); + if(!mApp->getSettingsValid()) + warn.add(F("your settings are invalid")); + if(mApp->getRebootRequestState()) + warn.add(F("reboot your ESP to apply all your configuration changes")); + if(0 == mApp->getTimestamp()) + warn.add(F("time not set. No communication to inverter possible")); + /*if(0 == mSys->getNumInverters()) + warn.add(F("no inverter configured"));*/ if((!mApp->getMqttIsConnected()) && (String(mConfig->mqtt.broker).length() > 0)) warn.add(F("MQTT is not connected")); JsonArray info = obj.createNestedArray(F("infos")); - if(mApp->getRebootRequestState()) - info.add(F("reboot your ESP to apply all your configuration changes!")); - if(!mApp->getSettingsValid()) - info.add(F("your settings are invalid")); if(mApp->getMqttIsConnected()) info.add(F("MQTT is connected, ") + String(mApp->getMqttTxCnt()) + F(" packets sent, ") + String(mApp->getMqttRxCnt()) + F(" packets received")); } void getSetup(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); + getGeneric(obj.createNestedObject(F("generic"))); getSysInfo(obj.createNestedObject(F("system"))); getInverterList(obj.createNestedObject(F("inverter"))); getMqtt(obj.createNestedObject(F("mqtt"))); @@ -402,7 +413,7 @@ class RestApi { void getLive(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); - getSysInfo(obj.createNestedObject(F("system"))); + getGeneric(obj.createNestedObject(F("generic"))); JsonArray invArr = obj.createNestedArray(F("inverter")); obj["refresh_interval"] = mConfig->nrf.sendInterval; diff --git a/src/web/html/api.js b/src/web/html/api.js index d74fb0b7..0606dfa8 100644 --- a/src/web/html/api.js +++ b/src/web/html/api.js @@ -1,4 +1,36 @@ /** + * SVG ICONS + */ + +iconWifi1 = [ + "M11.046 10.454c.226-.226.185-.605-.1-.75A6.473 6.473 0 0 0 8 9c-1.06 0-2.062.254-2.946.704-.285.145-.326.524-.1.75l.015.015c.16.16.407.19.611.09A5.478 5.478 0 0 1 8 10c.868 0 1.69.201 2.42.56.203.1.45.07.611-.091l.015-.015zM9.06 12.44c.196-.196.198-.52-.04-.66A1.99 1.99 0 0 0 8 11.5a1.99 1.99 0 0 0-1.02.28c-.238.14-.236.464-.04.66l.706.706a.5.5 0 0 0 .707 0l.708-.707z" +]; + +iconWifi2 = [ + "M13.229 8.271c.216-.216.194-.578-.063-.745A9.456 9.456 0 0 0 8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065A8.46 8.46 0 0 1 8 7a8.46 8.46 0 0 1 4.577 1.336c.205.132.48.108.652-.065zm-2.183 2.183c.226-.226.185-.605-.1-.75A6.473 6.473 0 0 0 8 9c-1.06 0-2.062.254-2.946.704-.285.145-.326.524-.1.75l.015.015c.16.16.408.19.611.09A5.478 5.478 0 0 1 8 10c.868 0 1.69.201 2.42.56.203.1.45.07.611-.091l.015-.015zM9.06 12.44c.196-.196.198-.52-.04-.66A1.99 1.99 0 0 0 8 11.5a1.99 1.99 0 0 0-1.02.28c-.238.14-.236.464-.04.66l.706.706a.5.5 0 0 0 .708 0l.707-.707z" +]; + +iconWifi3 = [ + "M15.384 6.115a.485.485 0 0 0-.047-.736A12.444 12.444 0 0 0 8 3C5.259 3 2.723 3.882.663 5.379a.485.485 0 0 0-.048.736.518.518 0 0 0 .668.05A11.448 11.448 0 0 1 8 4c2.507 0 4.827.802 6.716 2.164.205.148.49.13.668-.049z", + "M13.229 8.271a.482.482 0 0 0-.063-.745A9.455 9.455 0 0 0 8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065A8.46 8.46 0 0 1 8 7a8.46 8.46 0 0 1 4.576 1.336c.206.132.48.108.653-.065zm-2.183 2.183c.226-.226.185-.605-.1-.75A6.473 6.473 0 0 0 8 9c-1.06 0-2.062.254-2.946.704-.285.145-.326.524-.1.75l.015.015c.16.16.407.19.611.09A5.478 5.478 0 0 1 8 10c.868 0 1.69.201 2.42.56.203.1.45.07.61-.091l.016-.015zM9.06 12.44c.196-.196.198-.52-.04-.66A1.99 1.99 0 0 0 8 11.5a1.99 1.99 0 0 0-1.02.28c-.238.14-.236.464-.04.66l.706.706a.5.5 0 0 0 .707 0l.707-.707z" +]; + +iconWarn = [ + "M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.146.146 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.163.163 0 0 1-.054.06.116.116 0 0 1-.066.017H1.146a.115.115 0 0 1-.066-.017.163.163 0 0 1-.054-.06.176.176 0 0 1 .002-.183L7.884 2.073a.147.147 0 0 1 .054-.057zm1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566z", + "M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995z" +]; + +iconInfo = [ + "M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z", + "m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" +]; + +iconSuccess = [ + "M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z", + "M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" +]; + + /** * GENERIC FUNCTIONS */ @@ -31,6 +63,15 @@ function parseESP(obj) { document.getElementById("esp_type").innerHTML="Board: " + obj["esp_type"]; } +function parseRssi(obj) { + var icon = iconWifi3; + if(obj["wifi_rssi"] <= -80) + icon = iconWifi1; + else if(obj["wifi_rssi"] <= -70) + icon = iconWifi2; + document.getElementById("wifiicon").replaceChildren(svg(icon, 32, 32, "#fff", null, obj["wifi_rssi"])); +} + function setHide(id, hide) { var elm = document.getElementById(id); if(hide) { @@ -99,15 +140,11 @@ function inp(name, val, max=32, cl=["text"], id=null, type=null) { return e; } -function sel(name, opt, selId) { +function sel(name, options, selId) { e = document.createElement('select'); e.name = name; - for(it of opt) { - o = document.createElement('option'); - o.value = it[0]; - o.innerHTML = it[1]; - if(it[0] == selId) - o.selected = true; + for(it of options) { + o = opt(it[0], it[1], (it[0] == selId)); e.appendChild(o); } return e; @@ -120,11 +157,12 @@ function selDelAllOpt(sel) { } } -function opt(val, html) { +function opt(val, html, sel=false) { o = document.createElement('option'); o.value = val; o.innerHTML = html; - e.appendChild(o); + if(sel) + o.selected = true; return o; } @@ -156,3 +194,25 @@ function link(dst, text, target=null) { a.appendChild(t); return a; } + +function svg(data=null, w=24, h=24, color="#000", cl=null, tooltip=null) { + var s = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + s.setAttribute('width', w); + s.setAttribute('height', h); + s.setAttribute('fill', color); + s.setAttribute('viewBox', '0 0 16 16'); + if(null != cl) s.setAttribute('class', cl); + if(null != data) { + for(const e of data) { + const i = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + i.setAttribute('d', e); + s.appendChild(i); + } + } + if(null != tooltip) { + const t = document.createElement("title"); + t.appendChild(document.createTextNode(tooltip)); + s.appendChild(t); + } + return s; +} diff --git a/src/web/html/index.html b/src/web/html/index.html index d626f25a..065f5f8d 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -15,6 +15,7 @@
+
@@ -35,23 +36,18 @@ Uptime:
ESP-Time:

-
- Communication
- start:
- stop: -
-

WiFi RSSI: dBm

- System Infos: -


-                    

-                    

+                    System Infos:
+                    
+
+

+
- Discuss with us on Discord

Support this project:

    +
  • Discuss with us on Discord
  • Report issues
  • Contribute to documentation
  • Test development firmware
  • @@ -83,6 +79,7 @@ var exeOnce = true; var tickCnt = 0; var ts = 0; + var commInfo = ""; function apiCb(obj) { var e = document.getElementById("apiResult"); @@ -102,24 +99,28 @@ getAjax("/api/setup", apiCb, "POST", JSON.stringify(obj)); } - function parseSys(obj) { + function ts2Span(ts) { + return span(new Date(ts * 1000).toLocaleString('de-DE')); + } + + function parseGeneric(obj) { // Disclaimer //if(obj["disclaimer"] == false) sessionStorage.setItem("gDisclaimer", promptFunction()); if(exeOnce){ parseVersion(obj); parseESP(obj); } - document.getElementById("wifi_rssi").innerHTML = obj["wifi_rssi"]; + parseRssi(obj); + } + function parseSys(obj) { ts = obj["ts_now"]; var date = new Date(obj["ts_now"] * 1000); - var up = obj["ts_uptime"]; + var up = obj["generic"]["ts_uptime"]; var days = parseInt(up / 86400) % 365; var hrs = parseInt(up / 3600) % 24; var min = parseInt(up / 60) % 60; var sec = up % 60; - var sunrise = new Date(obj["ts_sunrise"] * 1000); - var sunset = new Date(obj["ts_sunset"] * 1000); var e = document.getElementById("uptime"); e.innerHTML = days + " Day"; if(1 != days) @@ -139,60 +140,73 @@ e.addEventListener("click", setTime); } - if(0 == obj["ts_sunrise"]) { - var e = document.getElementById("sun"); - if(null != e) - e.parentNode.removeChild(e); - } - else { - document.getElementById("sunrise").innerHTML = sunrise.toLocaleString('de-DE'); - document.getElementById("sunset").innerHTML = sunset.toLocaleString('de-DE'); + if(obj["ts_sunrise"] > 0) { + if(((obj["ts_sunrise"] - obj["ts_offset"]) < obj["ts_now"]) + && ((obj["ts_sunset"] + obj["ts_offset"]) > obj["ts_now"])) { + commInfo = "Polling inverter(s), will stop at " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE')); + } + else { + commInfo = "Night time, no Communication to Inverter, "; + if(obj["ts_now"] > (obj["ts_sunrise"] - obj["ts_offset"])) { + commInfo += "stopped polling at " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE')); + } + else { + commInfo += "will start polling at " + (new Date((obj["ts_sunrise"] - obj["ts_offset"]) * 1000).toLocaleString('de-DE')); + } + } } } - function parseStat(obj) { - document.getElementById("stat").innerHTML = "RX success: " + obj["rx_success"] - + "\nRX fail: " + obj["rx_fail"] - + "\nRX no answer: " + obj["rx_fail_answer"] - + "\nFrames received: " + obj["frame_cnt"] - + "\nTX cnt: " + obj["tx_cnt"]; - } - function parseIv(obj) { - var html = ""; + var p = div(["none"]); for(var i of obj) { - html += "Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is "; - if(false == i["is_avail"]) - html += "not yet available\n"; + var icon = iconWarn; + var color = "#F70"; + avail = ""; + if(false == i["enabled"]) { + avail = "disabled"; + } + else if(false == i["is_avail"]) { + icon = iconInfo; + color = "#00d"; + avail = "not yet available"; + } else { - html += "available and is "; + icon = iconSuccess; + avail = "available and is "; if(false == i["is_producing"]) - html += "not "; - html += "producing\n"; + avail += "not "; + avail += "producing"; } + p.append( + svg(icon, 20, 20, color, "icon"), + span("Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is " + avail), + br() + ); + if(false == i["is_avail"]) { if(i["ts_last_success"] > 0) { var date = new Date(i["ts_last_success"] * 1000); - html += "-> last successful transmission: " + date.toLocaleString('de-DE') + "\n"; + p.append(span("-> last successful transmission: " + date.toLocaleString('de-DE')), br()); } } - if(false == i["enabled"]) - html += "-> disabled\n"; - } - document.getElementById("iv").innerHTML = html; + document.getElementById("iv").replaceChildren(p); } - function parseWarnInfo(warn, info) { - var html = ""; + function parseWarnInfo(warn, success) { + var p = div(["none"]); for(var w of warn) { - html += "WARN: " + w + "\n"; + p.append(svg(iconWarn, 20, 20, "#F70", "icon"), span(w), br()); } - for(var i of info) { - html += "INFO: " + i + "\n"; + for(var i of success) { + p.append(svg(iconSuccess, 20, 20, "#090", "icon"), span(i), br()); } - document.getElementById("warn_info").innerHTML = html; + + if(commInfo.length > 0) + p.append(svg(iconInfo, 20, 20, "#00d", "icon"), span(commInfo), br()); + document.getElementById("warn_info").replaceChildren(p); } function tick() { @@ -208,8 +222,8 @@ if(null != obj) { if(exeOnce) parseMenu(obj["menu"]); - parseSys(obj["system"]); - parseStat(obj["statistics"]); + parseGeneric(obj["generic"]); + parseSys(obj); parseIv(obj["inverter"]); parseWarnInfo(obj["warnings"], obj["infos"]); if(exeOnce) { @@ -217,8 +231,6 @@ exeOnce = false; } } - else - document.getElementById("refresh").innerHTML = "n/a"; } getAjax("/api/index", parse); diff --git a/src/web/html/login.html b/src/web/html/login.html index 68f44084..6161461f 100644 --- a/src/web/html/login.html +++ b/src/web/html/login.html @@ -33,10 +33,10 @@
diff --git a/src/web/html/serial.html b/src/web/html/serial.html index ea57f73f..a102b58c 100644 --- a/src/web/html/serial.html +++ b/src/web/html/serial.html @@ -15,6 +15,7 @@
+
@@ -83,7 +84,7 @@ var con = document.getElementById("serial"); var exeOnce = true; - function parseSys(obj) { + function parseGeneric(obj) { var up = obj["ts_uptime"]; var days = parseInt(up / 86400) % 365; var hrs = parseInt(up / 3600) % 24; @@ -94,10 +95,11 @@ + ("0"+min).substr(-2) + ":" + ("0"+sec).substr(-2); + parseRssi(obj); if(true == exeOnce) { parseVersion(obj); parseESP(obj); - window.setInterval("getAjax('/api/system', parseSys)", 10000); + window.setInterval("getAjax('/api/generic', parseGeneric)", 10000); exeOnce = false; getAjax("/api/setup", parse); } @@ -201,7 +203,7 @@ getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); }); - getAjax("/api/system", parseSys); + getAjax("/api/generic", parseGeneric); diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 3a83c5ee..0790c4bf 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -26,6 +26,7 @@
+
@@ -41,7 +42,7 @@ - +
WiFi @@ -116,6 +117,8 @@ + +

@@ -337,8 +340,12 @@ var e = document.getElementsByName("adminpwd")[0]; if(!obj["pwd_set"]) e.value = ""; + } + + function parseGeneric(obj) { parseVersion(obj); parseESP(obj); + parseRssi(obj); } function parseStaticIp(obj) { @@ -368,6 +375,10 @@ document.getElementsByName("sunLat")[0].value = obj["lat"]; document.getElementsByName("sunLon")[0].value = obj["lon"]; document.getElementsByName("sunDisNightCom")[0].checked = obj["disnightcom"]; + const sel = document.getElementsByName("sunOffs")[0]; + for(var i = 0; i <= 60; i++) { + sel.appendChild(opt(i, i + " minutes", (i == (obj["offs"] / 60)))); + } } function parsePinout(obj, type) { @@ -452,6 +463,7 @@ if(null != root) { parseMenu(root["menu"]); parseSys(root["system"]); + parseGeneric(root["generic"]); parseStaticIp(root["static_ip"]); parseIv(root["inverter"]); parseMqtt(root["mqtt"]); diff --git a/src/web/html/style.css b/src/web/html/style.css index 87b2c4a0..8f4b473b 100644 --- a/src/web/html/style.css +++ b/src/web/html/style.css @@ -31,11 +31,11 @@ h2 { } .topnav a.icon { + top: 0; + left: 0; background: #333; display: block; position: absolute; - left: 0; - top: 0; } .topnav a:hover { @@ -43,6 +43,20 @@ h2 { color: #000; } +.topnav .info { + color: #fff; + position: absolute; + right: 24px; + top: 5px; +} + +svg.icon { + vertical-align: middle; + display: inline-block; + margin-top:-4x; + padding: 5px 7px 5px 0px; +} + .title { background-color: #006ec0; color: #fff !important; @@ -142,6 +156,13 @@ span.seperator { display: block; } + .topnav .info { + top: auto !important; + right: auto !important; + bottom: 14px; + left: 24px; + } + #content { padding: 15px 15px 120px 250px; } @@ -376,11 +397,16 @@ div.ModPwr, div.ModName { display: inline-block; } +div.hr { + height: 1px; + border-top: 1px solid #ccc; + margin: 10px 0px 10px; +} + #note { - margin: 50px 10px 10px 10px; + margin: 10px 10px 10px 10px; padding-top: 10px; width: 100%; - border-top: 1px solid #bbb; } @media(max-width: 500px) { @@ -477,6 +503,12 @@ div.ModPwr, div.ModName { .mt-4 { margin-top: 1.5rem } .mt-5 { margin-top: 3rem } +.mb-1 { margin-bottom: 0.25rem } +.mb-2 { margin-bottom: 0.5rem } +.mb-3 { margin-bottom: 1rem } +.mb-4 { margin-bottom: 1.5rem } +.mb-5 { margin-bottom: 3rem } + .a-r { text-align: right; } .a-c { text-align: center; } diff --git a/src/web/html/system.html b/src/web/html/system.html index 7303bd0a..12d48a8f 100644 --- a/src/web/html/system.html +++ b/src/web/html/system.html @@ -15,12 +15,15 @@
+
+

                 
-
+
+