diff --git a/src/CHANGES.md b/src/CHANGES.md index c03de595..c4f55659 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -3,6 +3,7 @@ ## 0.8.134 - 2024-08-10 * combined Ethernet and WiFi variants - Ethernet is now always included, but needs to be enabled if needed * improved statistic data in `/system` +* redesigned `/system` ## 0.8.133 - 2024-08-10 * Ethernet variants now support WiFi as fall back / configuration diff --git a/src/app.h b/src/app.h index 9a5e4329..d23f307e 100644 --- a/src/app.h +++ b/src/app.h @@ -187,6 +187,10 @@ class app : public IApp, public ah::Scheduler { return mNetwork->getIp(); } + String getMac(void) override { + return mNetwork->getMac(); + } + bool isApActive(void) override { return mNetwork->isApActive(); } diff --git a/src/appInterface.h b/src/appInterface.h index e2e75c59..d49f907e 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -32,6 +32,7 @@ class IApp { virtual void setupStation(void) = 0; virtual bool getWasInCh12to14(void) const = 0; virtual String getIp(void) = 0; + virtual String getMac(void) = 0; virtual bool isApActive(void) = 0; virtual uint32_t getUptime() = 0; diff --git a/src/network/AhoyEthernet.h b/src/network/AhoyEthernet.h index 43565169..c8ee1d81 100644 --- a/src/network/AhoyEthernet.h +++ b/src/network/AhoyEthernet.h @@ -41,6 +41,13 @@ class AhoyEthernet : public AhoyWifi { return ETH.localIP().toString(); } + String getMac(void) { + if(Mode::WIRELESS == mMode) + return AhoyWifi::getMac(); + else + return ETH.macAddress(); + } + bool isWiredConnection() { return (Mode::WIRED == mMode); } diff --git a/src/network/AhoyNetwork.h b/src/network/AhoyNetwork.h index 76ad3317..e64a13b1 100644 --- a/src/network/AhoyNetwork.h +++ b/src/network/AhoyNetwork.h @@ -79,6 +79,7 @@ class AhoyNetwork { virtual void begin() = 0; virtual void tickNetworkLoop() = 0; virtual String getIp(void) = 0; + virtual String getMac(void) = 0; virtual bool getWasInCh12to14() { return false; diff --git a/src/network/AhoyWifiEsp32.h b/src/network/AhoyWifiEsp32.h index f4792eab..b3b1073f 100644 --- a/src/network/AhoyWifiEsp32.h +++ b/src/network/AhoyWifiEsp32.h @@ -36,6 +36,20 @@ class AhoyWifi : public AhoyNetwork { #endif } + void tickNetworkLoop() override { + if(mAp.isEnabled()) + mAp.tickLoop(); + } + + String getIp(void) override { + return WiFi.localIP().toString(); + } + + String getMac(void) override { + return WiFi.macAddress(); + } + + private: virtual void OnEvent(WiFiEvent_t event) override { switch(event) { case SYSTEM_EVENT_STA_CONNECTED: @@ -78,16 +92,6 @@ class AhoyWifi : public AhoyNetwork { } } - void tickNetworkLoop() override { - if(mAp.isEnabled()) - mAp.tickLoop(); - } - - String getIp(void) override { - return WiFi.localIP().toString(); - } - - private: virtual void setStaticIp() override { setupIp([this](IPAddress ip, IPAddress gateway, IPAddress mask, IPAddress dns1, IPAddress dns2) -> bool { return WiFi.config(ip, gateway, mask, dns1, dns2); diff --git a/src/network/AhoyWifiEsp8266.h b/src/network/AhoyWifiEsp8266.h index b6ea65d2..c72f06b5 100644 --- a/src/network/AhoyWifiEsp8266.h +++ b/src/network/AhoyWifiEsp8266.h @@ -111,6 +111,10 @@ class AhoyWifi : public AhoyNetwork { return WiFi.localIP().toString(); } + String getMac(void) override { + return WiFi.macAddress(); + } + bool getWasInCh12to14() override { return mWasInCh12to14; } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 8f48d395..3cd036dd 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -413,11 +413,6 @@ class RestApi { } void getSysInfo(AsyncWebServerRequest *request, JsonObject obj) { - obj[F("ap_pwd")] = mConfig->sys.apPwd; - obj[F("ssid")] = mConfig->sys.stationSsid; - obj[F("hidd")] = mConfig->sys.isHidden; - obj[F("mac")] = WiFi.macAddress(); - obj[F("wifi_channel")] = WiFi.channel(); obj[F("device_name")] = mConfig->sys.deviceName; obj[F("dark_mode")] = (bool)mConfig->sys.darkMode; obj[F("sched_reboot")] = (bool)mConfig->sys.schedReboot; @@ -425,97 +420,15 @@ class RestApi { obj[F("pwd_set")] = (strlen(mConfig->sys.adminPwd) > 0); obj[F("prot_mask")] = mConfig->sys.protectionMask; - obj[F("sdk")] = ESP.getSdkVersion(); - obj[F("cpu_freq")] = ESP.getCpuFreqMHz(); - obj[F("heap_free")] = mHeapFree; - getGeneric(request, obj); - + getGeneric(request, obj.createNestedObject(F("generic"))); + getChipInfo(obj.createNestedObject(F("chip"))); getRadioNrf(obj.createNestedObject(F("radioNrf"))); + getMqttInfo(obj.createNestedObject(F("mqtt"))); + getNetworkInfo(obj.createNestedObject(F("network"))); + getMemoryInfo(obj.createNestedObject(F("memory"))); #if defined(ESP32) getRadioCmtInfo(obj.createNestedObject(F("radioCmt"))); #endif - getMqttInfo(obj.createNestedObject(F("mqtt"))); - - #if defined(ETHERNET) - getEthernetInfo(obj.createNestedObject(F("eth"))); - #endif - - obj[F("heap_frag")] = mHeapFrag; - obj[F("max_free_blk")] = mHeapFreeBlk; - - #if defined(ESP32) - obj[F("chip_revision")] = ESP.getChipRevision(); - obj[F("chip_model")] = ESP.getChipModel(); - obj[F("chip_cores")] = ESP.getChipCores(); - obj[F("heap_total")] = ESP.getHeapSize(); - //obj[F("core_version")] = F("n/a"); - - esp_reset_reason_t reason = esp_reset_reason(); - - // Reboot-Grund ausgeben - switch (reason) { - default: - [[fallthrough]]; - case ESP_RST_UNKNOWN: - obj[F("reboot_reason")] = F("Unknown"); - break; - case ESP_RST_POWERON: - obj[F("reboot_reason")] = F("Power on"); - break; - case ESP_RST_EXT: - obj[F("reboot_reason")] = F("External"); - break; - case ESP_RST_SW: - obj[F("reboot_reason")] = F("Software"); - break; - case ESP_RST_PANIC: - obj[F("reboot_reason")] = F("Panic"); - break; - case ESP_RST_INT_WDT: - obj[F("reboot_reason")] = F("Interrupt Watchdog"); - break; - case ESP_RST_TASK_WDT: - obj[F("reboot_reason")] = F("Task Watchdog"); - break; - case ESP_RST_WDT: - obj[F("reboot_reason")] = F("Watchdog"); - break; - case ESP_RST_DEEPSLEEP: - obj[F("reboot_reason")] = F("Deepsleep"); - break; - case ESP_RST_BROWNOUT: - obj[F("reboot_reason")] = F("Brownout"); - break; - case ESP_RST_SDIO: - obj[F("reboot_reason")] = F("SDIO"); - break; - } - - const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, "coredump"); - if (partition != NULL) - obj[F("flash_size")] = partition->address + partition->size; - #else - //obj[F("heap_total")] = F("n/a"); - //obj[F("chip_revision")] = F("n/a"); - //obj[F("chip_model")] = F("n/a"); - //obj[F("chip_cores")] = F("n/a"); - obj[F("core_version")] = ESP.getCoreVersion(); - obj[F("flash_size")] = ESP.getFlashChipRealSize() / 1024; // in kb - obj[F("reboot_reason")] = ESP.getResetReason(); - #endif - - #if !defined(ESP32) - FSInfo info; - LittleFS.info(info); - obj[F("par_used_spiffs")] = info.usedBytes; - obj[F("par_size_spiffs")] = info.totalBytes; - #else - obj[F("par_size_spiffs")] = LittleFS.totalBytes(); - obj[F("par_used_spiffs")] = LittleFS.usedBytes(); - #endif - - obj[F("par_size_app0")] = ESP.getFreeSketchSpace(); - obj[F("par_used_app0")] = ESP.getSketchSize(); /*uint8_t max; mApp->getSchedulerInfo(&max); @@ -885,11 +798,106 @@ class RestApi { obj[F("irq")] = mConfig->sys.eth.pinIrq; obj[F("reset")] = mConfig->sys.eth.pinRst; } + #endif + + void getNetworkInfo(JsonObject obj) { + #if defined(ESP32) + bool isWired = mApp->isWiredConnection(); + if(!isWired) + obj[F("wifi_channel")] = WiFi.channel(); + #else + obj[F("wifi_channel")] = WiFi.channel(); + #endif - void getEthernetInfo(JsonObject obj) { - obj[F("wired")] = (bool)mApp->isWiredConnection(); + obj[F("ap_pwd")] = mConfig->sys.apPwd; + obj[F("ssid")] = mConfig->sys.stationSsid; + obj[F("hidd")] = mConfig->sys.isHidden; + obj[F("mac")] = mApp->getMac(); + obj[F("ip")] = mApp->getIp(); + + #if defined(ESP32) + obj[F("wired")] = isWired; + #endif + } + + void getChipInfo(JsonObject obj) { + obj[F("cpu_freq")] = ESP.getCpuFreqMHz(); + obj[F("sdk")] = ESP.getSdkVersion(); + #if defined(ESP32) + obj[F("revision")] = ESP.getChipRevision(); + obj[F("model")] = ESP.getChipModel(); + obj[F("cores")] = ESP.getChipCores(); + + switch (esp_reset_reason()) { + default: + [[fallthrough]]; + case ESP_RST_UNKNOWN: + obj[F("reboot_reason")] = F("Unknown"); + break; + case ESP_RST_POWERON: + obj[F("reboot_reason")] = F("Power on"); + break; + case ESP_RST_EXT: + obj[F("reboot_reason")] = F("External"); + break; + case ESP_RST_SW: + obj[F("reboot_reason")] = F("Software"); + break; + case ESP_RST_PANIC: + obj[F("reboot_reason")] = F("Panic"); + break; + case ESP_RST_INT_WDT: + obj[F("reboot_reason")] = F("Interrupt Watchdog"); + break; + case ESP_RST_TASK_WDT: + obj[F("reboot_reason")] = F("Task Watchdog"); + break; + case ESP_RST_WDT: + obj[F("reboot_reason")] = F("Watchdog"); + break; + case ESP_RST_DEEPSLEEP: + obj[F("reboot_reason")] = F("Deepsleep"); + break; + case ESP_RST_BROWNOUT: + obj[F("reboot_reason")] = F("Brownout"); + break; + case ESP_RST_SDIO: + obj[F("reboot_reason")] = F("SDIO"); + break; + } + #else + obj[F("core_version")] = ESP.getCoreVersion(); + obj[F("reboot_reason")] = ESP.getResetReason(); + #endif + } + + void getMemoryInfo(JsonObject obj) { + obj[F("heap_frag")] = mHeapFrag; + obj[F("heap_max_free_blk")] = mHeapFreeBlk; + obj[F("heap_free")] = mHeapFree; + + obj[F("par_size_app0")] = ESP.getFreeSketchSpace(); + obj[F("par_used_app0")] = ESP.getSketchSize(); + + #if defined(ESP32) + obj[F("heap_total")] = ESP.getHeapSize(); + + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, "coredump"); + if (partition != NULL) + obj[F("flash_size")] = partition->address + partition->size; + + obj[F("par_size_spiffs")] = LittleFS.totalBytes(); + obj[F("par_used_spiffs")] = LittleFS.usedBytes(); + #else + obj[F("flash_size")] = ESP.getFlashChipRealSize(); + + FSInfo info; + LittleFS.info(info); + obj[F("par_used_spiffs")] = info.usedBytes; + obj[F("par_size_spiffs")] = info.totalBytes; + obj[F("heap_total")] = 24*1014; // FIXME: don't know correct value + #endif } - #endif void getRadioNrf(JsonObject obj) { obj[F("en")] = (bool) mConfig->nrf.enabled; diff --git a/src/web/html/api.js b/src/web/html/api.js index 4930ce17..12ab239b 100644 --- a/src/web/html/api.js +++ b/src/web/html/api.js @@ -244,6 +244,10 @@ function badge(success, text, second="error") { return ml("span", {class: "badge badge-" + ((success) ? "success" : second)}, text); } +function progress(val) { + return ml("div", {class: "progress"}, ml("div", {class: "progress-bar", style: "width: " + val + "%"}, null)) +} + function tabChange(id) { var els = document.getElementsByClassName("nav-link"); [].forEach.call(els, function(e) { diff --git a/src/web/html/style.css b/src/web/html/style.css index cda2df23..6f05ea77 100644 --- a/src/web/html/style.css +++ b/src/web/html/style.css @@ -859,3 +859,16 @@ ul { height: 100%; overflow: auto; } + +.progress { + display: flex; + height: 1rem; + overflow: hidden; + background-color: #e9ecef; + border-radius: .25rem; +} + +.progress-bar { + display: flex; + background-color: var(--primary); +} diff --git a/src/web/html/system.html b/src/web/html/system.html index 692af912..b6516970 100644 --- a/src/web/html/system.html +++ b/src/web/html/system.html @@ -8,7 +8,7 @@ {#HTML_NAV}
-
+
@@ -21,17 +21,31 @@ parseTitle(obj) } + function parseUptime(up) { + var days = parseInt(up / 86400) % 365 + var hrs = parseInt(up / 3600) % 24 + var min = parseInt(up / 60) % 60 + var sec = up % 60 + var str = days + " day" + if(1 != days) + str += "s" + str += ", " + ("0"+hrs).substr(-2) + ":" + + ("0"+min).substr(-2) + ":" + + ("0"+sec).substr(-2) + + return ml("span", {}, str) + } + function parseSysInfo(obj) { - const data = ["sdk", "cpu_freq", "chip_revision", "device_name", - "chip_model", "chip_cores", "esp_type", "mac", "wifi_rssi", "wifi_channel", "ts_uptime", - "flash_size", "sketch_used", "heap_total", "heap_free", "heap_frag", - "max_free_blk", "version", "modules", "env", "core_version", "reboot_reason", "is_eth_con"]; - - lines = []; - for (const [key, value] of Object.entries(obj)) { - if(!data.includes(key) || (typeof value == 'undefined')) continue; - lines.push(tr(key.replace('_', ' '), value)); - } + lines = [ + tr("{#DEVICE_NAME}", obj.device_name), + tr("{#UPTIME}", parseUptime(obj.generic.ts_uptime)), + tr("{#REBOOT_REASON}", obj.chip.reboot_reason), + tr("{#ENVIRONMENT}", obj.generic.env + " ({#BUILD_OPTIONS}: " + obj.generic.modules + ")"), + tr("Version", obj.generic.version + " - " + obj.generic.build), + tr("Chip", "CPU: " + obj.chip.cpu_freq + "MHz, " + obj.chip.cores + " Core(s)"), + tr("Chip Model", obj.chip.model) + ] document.getElementById("info").append( headline("System Information"), @@ -47,9 +61,9 @@ function irqBadge(state) { switch(state) { - case 0: return badge(false, "{#UNKNOWN}", "warning"); break; - case 1: return badge(true, "{#TRUE}"); break; - default: return badge(false, "{#FALSE}"); break; + case 0: return badge(false, "unknown", "warning"); break; + case 1: return badge(true, "true"); break; + default: return badge(false, "false"); break; } } @@ -58,16 +72,16 @@ if(obj.radioNrf.en) { lines = [ - tr("NRF24L01", badge(obj.radioNrf.isconnected, ((obj.radioNrf.isconnected) ? "" : "{#NOT} ") + "{#CONNECTED}")), - tr("{#IRQ_WORKING}", irqBadge(obj.radioNrf.irqOk)), - tr("{#NRF24_DATA_RATE}", dr[obj.radioNrf.dataRate] + "bps"), + tr("NRF24L01", badge(obj.radioNrf.isconnected, ((obj.radioNrf.isconnected) ? "" : "not ") + "connected")), + tr("Interrupt Pin working", irqBadge(obj.radioNrf.irqOk)), + tr("NRF24 Data Rate", dr[obj.radioNrf.dataRate] + "bps"), tr("DTU Radio ID", obj.radioNrf.sn) ]; } else - lines = [tr("NRF24L01", badge(false, "{#NOT_ENABLED}"))]; + lines = [tr("NRF24L01", badge(false, "not enabled"))]; document.getElementById("info").append( - headline("{#NRF24_RADIO}"), + headline("Radio NRF24"), ml("table", {class: "table"}, ml("tbody", {}, lines) ) @@ -76,15 +90,15 @@ /*IF_ESP32*/ if(obj.radioCmt.en) { cmt = [ - tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "{#NOT} ") + "{#CONNECTED}")), - tr("{#IRQ_WORKING}", irqBadge(obj.radioCmt.irqOk)), + tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "not ") + "connected")), + tr("Interrupt Pin working", irqBadge(obj.radioCmt.irqOk)), tr("DTU Radio ID", obj.radioCmt.sn) ]; } else - cmt = [tr("CMT2300A", badge(false, "{#NOT_ENABLED}"))]; + cmt = [tr("CMT2300A", badge(false, "not enabled"))]; document.getElementById("info").append( - headline("{#CMT_RADIO}"), + headline("Radio CMT"), ml("table", {class: "table"}, ml("tbody", {}, cmt) ) @@ -92,16 +106,32 @@ /*ENDIF_ESP32*/ } + function parseNetwork(obj, gen) { + lines = [ + tr("connection", ((obj.wired) ? "{#WIRED}" : "{#WIFI} (SSID: " + obj.ssid + ", RSSI: " + gen.wifi_rssi + ", CH: " + obj.wifi_channel + ")")), + tr("Hostname", gen.host), + tr("IP {#ADDRESS}", obj.ip), + tr("MAC {#ADDRESS}", obj.mac) + ] + + document.getElementById("info").append( + headline("{#NETWORK}"), + ml("table", {class: "table"}, + ml("tbody", {}, lines) + ) + ); + } + function parseMqtt(obj) { if(obj.enabled) { lines = [ - tr("{#CONNECTED}", badge(obj.connected, ((obj.connected) ? "{#TRUE}" : "{#FALSE}"))), + tr("connected", badge(obj.connected, ((obj.connected) ? "true" : "false"))), tr("#TX", obj.tx_cnt), tr("#RX", obj.rx_cnt) - ]; + ] } else - lines = tr("{#ENABLED}", badge(false, "{#FALSE}")); + lines = tr("enabled", badge(false, "false")); document.getElementById("info").append( headline("MqTT"), @@ -111,30 +141,34 @@ ); } - /*IF_ETHERNET*/ - function parseEthernet(obj) { - lines = tr("{#CONNECTION_TYPE}", ((obj.wired) ? "{#WIRED}" : "{#WIFI}")); + function parseMemory(obj) { + lines = [ + tr("Flash size", obj.flash_size / 1024 / 1024 + "MB"), + tr("{#CONFIG_PARTITION} (" + Math.round(obj.par_used_spiffs / 1024) + "kB of " + obj.par_size_spiffs / 1024 + "kB)", progress(obj.par_used_spiffs / obj.par_size_spiffs * 100)), + tr("{#FIRMWARE_PARTITION} (" + Math.round(obj.par_used_app0 / 1024) + "kB of " + obj.par_size_app0 / 1024 + "kB)", progress(obj.par_used_app0 / obj.par_size_app0 * 100)), + tr("Heap (" + Math.round(obj.heap_free / 1024) + "kB of " + Math.round(obj.heap_total / 1024) + "kB)", progress(obj.heap_free / obj.heap_total * 100)), + tr("Heap max free block", Math.round(obj.heap_max_free_blk / 1024) + "kB (Fragmentation: " + obj.heap_frag + ")") + ] document.getElementById("info").append( - headline("{#NETWORK}"), + headline("{#MEMORY}"), ml("table", {class: "table"}, ml("tbody", {}, lines) ) ); } - /*ENDIF_ETHERNET*/ function parseIndex(obj) { if(obj.ts_sunrise > 0) { document.getElementById("info").append( - headline("{#SUN}"), + headline("Sun"), ml("table", {class: "table"}, ml("tbody", {}, [ - tr("{#SUNRISE}", new Date(obj.ts_sunrise * 1000).toLocaleString('de-DE')), - tr("{#SUNSET}", new Date(obj.ts_sunset * 1000).toLocaleString('de-DE')), - tr("{#COMMUNICATION_START}", new Date((obj.ts_sunrise + obj.ts_offsSr) * 1000).toLocaleString('de-DE')), - tr("{#COMMUNICATION_STOP}", new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE')), - tr("{#NIGHT_BEHAVE}", badge(obj.disNightComm, ((obj.disNightComm) ? "{#NOT}" : "") + " {#COMMUNICATING}", "warning")) + tr("sunrise", new Date(obj.ts_sunrise * 1000).toLocaleString('de-DE')), + tr("sunset", new Date(obj.ts_sunset * 1000).toLocaleString('de-DE')), + tr("Communication start", new Date((obj.ts_sunrise + obj.ts_offsSr) * 1000).toLocaleString('de-DE')), + tr("Communication stop", new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE')), + tr("Night behaviour", badge(obj.disNightComm, ((obj.disNightComm) ? "not" : "") + " communicating", "warning")) ]) ) ); @@ -152,10 +186,9 @@ document.getElementsByTagName('head')[0].appendChild(meta); } else if(null != obj.system) { parseRadio(obj.system) + parseNetwork(obj.system.network, obj.system.generic) parseMqtt(obj.system.mqtt) - /*IF_ETHERNET*/ - parseEthernet(obj.system.eth) - /*ENDIF_ETHERNET*/ + parseMemory(obj.system.memory) parseSysInfo(obj.system) getAjax('/api/index', parseIndex) } diff --git a/src/web/lang.json b/src/web/lang.json index ddc11cab..572deb35 100644 --- a/src/web/lang.json +++ b/src/web/lang.json @@ -927,6 +927,56 @@ "token": "WIFI", "en": "WiFi", "de": "WiFi" + }, + { + "token": "DEVICE_NAME", + "en": "Device name", + "de": "Gerätename" + }, + { + "token": "UPTIME", + "en": "Uptime", + "de": "Laufzeit" + }, + { + "token": "REBOOT_REASON", + "en": "Reboot reason", + "de": "Grund des Neustarts" + }, + { + "token": "ENVIRONMENT", + "en": "Environment", + "de": "Umgebung" + }, + { + "token": "BUILD_OPTIONS", + "en": "build options", + "de": "Module" + }, + { + "token": "ADDRESS", + "en": "Address", + "de": "Adresse" + }, + { + "token": "NETWORK", + "en": "Network", + "de": "Netzwerk" + }, + { + "token": "MEMORY", + "en": "Memory", + "de": "Speicher" + }, + { + "token": "CONFIG_PARTITION", + "en": "Config Partition", + "de": "Konfiguration" + }, + { + "token": "FIRMWARE_PARTITION", + "en": "Firmware Partition", + "de": "Firmware" } ] },