From 6a34490d4e699e400b97bf3005d8f7d46b78f670 Mon Sep 17 00:00:00 2001 From: fvanroie <15969459+fvanroie@users.noreply.github.com> Date: Sat, 1 May 2021 08:13:35 +0200 Subject: [PATCH] Add *_get_info in json format --- src/hasp/hasp.cpp | 77 ++++++++++++++++++++ src/hasp/hasp.h | 2 + src/mqtt/hasp_mqtt.h | 1 + src/mqtt/hasp_mqtt_paho_single.cpp | 33 ++++++++- src/mqtt/hasp_mqtt_pubsubclient.cpp | 34 +++++++++ src/sys/net/hasp_ethernet_esp32.cpp | 21 ++++++ src/sys/net/hasp_ethernet_esp32.h | 4 ++ src/sys/net/hasp_ethernet_stm32.h | 4 ++ src/sys/net/hasp_network.cpp | 11 +++ src/sys/net/hasp_network.h | 1 + src/sys/net/hasp_wifi.cpp | 45 ++++++++++++ src/sys/net/hasp_wifi.h | 2 + src/sys/svc/hasp_http.cpp | 107 ++++++++++++++++------------ 13 files changed, 295 insertions(+), 47 deletions(-) diff --git a/src/hasp/hasp.cpp b/src/hasp/hasp.cpp index 226d9383..94ea8b7d 100644 --- a/src/hasp/hasp.cpp +++ b/src/hasp/hasp.cpp @@ -573,6 +573,83 @@ void haspSetPage(uint8_t pageid) } } +void hasp_get_info(JsonDocument& doc) +{ + std::string buffer; + buffer.reserve(64); + char size_buf[32]; + JsonObject info = doc.createNestedObject(F(D_MANUFACTURER)); + + haspGetVersion(size_buf, sizeof(size_buf)); + info[F("Version")] = size_buf; + + buffer = __DATE__; + buffer += (" "); + buffer += __TIME__; + buffer += (" UTC"); // Github buildservers are in UTC + info[F("Build DateTime")] = buffer; + + unsigned long time = millis() / 1000; + uint16_t day = time / 86400; + time = time % 86400; + uint8_t hour = time / 3600; + time = time % 3600; + uint8_t min = time / 60; + time = time % 60; + uint8_t sec = time; + + buffer.clear(); + if(day > 0) { + itoa(day, size_buf, DEC); + buffer += size_buf; + buffer += "d "; + } + if(day > 0 || hour > 0) { + itoa(hour, size_buf, DEC); + buffer += size_buf; + buffer += "h "; + } + if(day > 0 || hour > 0 || min > 0) { + itoa(min, size_buf, DEC); + buffer += size_buf; + buffer += "m "; + } + itoa(sec, size_buf, DEC); + buffer += size_buf; + buffer += "s"; + info[F("Uptime")] = buffer; + + info = doc.createNestedObject(F("Device Memory")); + Parser::format_bytes(haspDevice.get_free_heap(), size_buf, sizeof(size_buf)); + info[F("Free Heap")] = size_buf; + Parser::format_bytes(haspDevice.get_free_max_block(), size_buf, sizeof(size_buf)); + info[F("Free Block")] = size_buf; + info[F("Fragmentation")] = haspDevice.get_heap_fragmentation(); + +#if ARDUINO_ARCH_ESP32 + if(psramFound()) { + Parser::format_bytes(ESP.getFreePsram(), size_buf, sizeof(size_buf)); + info[F("Free PSRam")] = size_buf; + Parser::format_bytes(ESP.getPsramSize(), size_buf, sizeof(size_buf)); + info[F("PSRam Size")] = size_buf; + } +#endif + + info = doc.createNestedObject(F("LVGL Memory")); + lv_mem_monitor_t mem_mon; + lv_mem_monitor(&mem_mon); + Parser::format_bytes(mem_mon.total_size, size_buf, sizeof(size_buf)); + info[F("Total")] = size_buf; + Parser::format_bytes(mem_mon.free_size, size_buf, sizeof(size_buf)); + info[F("Free")] = size_buf; + info[F("Fragmentation")] = mem_mon.frag_pct; + + info = doc.createNestedObject(F("HASP State")); + hasp_get_sleep_state(size_buf); + info[F("Idle")] = size_buf; + info[F("Active Page")] = haspPages.get(); +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if HASP_USE_CONFIG > 0 bool haspGetConfig(const JsonObject& settings) diff --git a/src/hasp/hasp.h b/src/hasp/hasp.h index 527c80e7..35311960 100644 --- a/src/hasp/hasp.h +++ b/src/hasp/hasp.h @@ -77,6 +77,8 @@ void hasp_disable_wakeup_touch(); void hasp_init(void); void hasp_load_json(void); +void hasp_get_info(JsonDocument& info); + /********************** * MACROS **********************/ diff --git a/src/mqtt/hasp_mqtt.h b/src/mqtt/hasp_mqtt.h index 92cda407..ff7a3cdc 100644 --- a/src/mqtt/hasp_mqtt.h +++ b/src/mqtt/hasp_mqtt.h @@ -34,6 +34,7 @@ int mqtt_send_discovery(const char* payload, size_t len); int mqttPublish(const char* topic, const char* payload, size_t len, bool retain); bool mqttIsConnected(); +void mqtt_get_info(JsonDocument& doc); #if HASP_USE_CONFIG > 0 bool mqttGetConfig(const JsonObject& settings); diff --git a/src/mqtt/hasp_mqtt_paho_single.cpp b/src/mqtt/hasp_mqtt_paho_single.cpp index 7165e33f..57f3a1c4 100644 --- a/src/mqtt/hasp_mqtt_paho_single.cpp +++ b/src/mqtt/hasp_mqtt_paho_single.cpp @@ -59,6 +59,9 @@ std::string mqttGroupTopic; std::string mqttLwtTopic; bool mqttEnabled = false; bool mqttHAautodiscover = true; +uint32_t mqttPublishCount; +uint32_t mqttReceiveCount; +uint32_t mqttFailedCount; //////////////////////////////////////////////////////////////////////////////////////////////////// // These defaults may be overwritten with values saved by the web interface @@ -117,9 +120,11 @@ void connlost(void* context, char* cause) static void mqtt_message_cb(char* topic, char* payload, size_t length) { // Handle incoming commands from MQTT if(length + 1 >= MQTT_MAX_PACKET_SIZE) { + mqttFailedCount++; LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), (uint32_t)length); return; } else { + mqttReceiveCount++; payload[length] = '\0'; } @@ -198,7 +203,10 @@ void mqtt_subscribe(void* context, const char* topic) int mqttPublish(const char* topic, const char* payload, size_t len, bool retain) { - if(!mqttIsConnected()) return MQTT_ERR_NO_CONN; + if(!mqttIsConnected()) { + mqttFailedCount++; + return MQTT_ERR_NO_CONN; + } MQTTClient_message pubmsg = MQTTClient_message_initializer; MQTTClient_deliveryToken token; @@ -212,10 +220,12 @@ int mqttPublish(const char* topic, const char* payload, size_t len, bool retain) int rc = MQTTClient_waitForCompletion(mqtt_client, token, TIMEOUT); // time to wait in milliseconds if(rc != MQTTCLIENT_SUCCESS) { + mqttFailedCount++; LOG_ERROR(TAG_MQTT_PUB, F(D_MQTT_FAILED " '%s' => %s"), topic, payload); return MQTT_ERR_PUB_FAIL; } else { // LOG_TRACE(TAG_MQTT_PUB, F("'%s' => %s OK"), topic, payload); + mqttPublishCount++; return MQTT_ERR_OK; } } @@ -383,5 +393,26 @@ void mqttLoop() void mqttEvery5Seconds(bool wifiIsConnected){}; +void mqtt_get_info(JsonDocument& doc) +{ + char mqttClientId[64]; + + JsonObject info = doc.createNestedObject(F("MQTT")); + info[F("Server")] = mqttServer; + info[F("User")] = mqttUser; + info[F("ClientID")] = haspDevice.get_hostname(); + + if(mqttIsConnected()) { // Check MQTT connection + info[F("Status")] = F("Connected"); + } else { + info[F("Status")] = F("Disconnected, return code: "); + // +String(mqttClient.returnCode()); + } + + info[F("Received")] = mqttReceiveCount; + info[F("Published")] = mqttPublishCount; + info[F("Failed")] = mqttFailedCount; +} + #endif // USE_PAHO #endif // USE_MQTT diff --git a/src/mqtt/hasp_mqtt_pubsubclient.cpp b/src/mqtt/hasp_mqtt_pubsubclient.cpp index c7dc9548..b25aaf2b 100644 --- a/src/mqtt/hasp_mqtt_pubsubclient.cpp +++ b/src/mqtt/hasp_mqtt_pubsubclient.cpp @@ -50,6 +50,9 @@ char mqttNodeTopic[24]; char mqttGroupTopic[24]; bool mqttEnabled = false; bool mqttHAautodiscover = true; +uint32_t mqttPublishCount; +uint32_t mqttReceiveCount; +uint32_t mqttFailedCount; //////////////////////////////////////////////////////////////////////////////////////////////////// // These defaults may be overwritten with values saved by the web interface @@ -95,11 +98,13 @@ int mqttPublish(const char* topic, const char* payload, size_t len, bool retain) if(!mqttClient.connected()) return MQTT_ERR_NO_CONN; if(mqttClient.beginPublish(topic, len, retain)) { + mqttPublishCount++; mqttClient.write((uint8_t*)payload, len); mqttClient.endPublish(); return MQTT_ERR_OK; } + mqttFailedCount++; return MQTT_ERR_PUB_FAIL; } @@ -156,9 +161,11 @@ int mqtt_send_discovery(const char* payload, size_t len) static void mqtt_message_cb(char* topic, byte* payload, unsigned int length) { // Handle incoming commands from MQTT if(length + 1 >= mqttClient.getBufferSize()) { + mqttFailedCount++; LOG_ERROR(TAG_MQTT_RCV, F(D_MQTT_PAYLOAD_TOO_LONG), (uint32_t)length); return; } else { + mqttReceiveCount++; payload[length] = '\0'; } @@ -372,6 +379,33 @@ void mqttStop() } } +void mqtt_get_info(JsonDocument& doc) +{ + char mqttClientId[64]; + String mac((char*)0); + mac.reserve(64); + + JsonObject info = doc.createNestedObject(F("MQTT")); + info[F("Server")] = mqttServer; + info[F("User")] = mqttUser; + + mac = halGetMacAddress(3, ""); + mac.toLowerCase(); + snprintf_P(mqttClientId, sizeof(mqttClientId), PSTR("%s-%s"), haspDevice.get_hostname(), mac.c_str()); + info[F("ClientID")] = mqttClientId; + + if(mqttIsConnected()) { // Check MQTT connection + info[F("Status")] = F("Connected"); + } else { + info[F("Status")] = F("Disconnected, return code: "); + // +String(mqttClient.returnCode()); + } + + info[F("Received")] = mqttReceiveCount; + info[F("Published")] = mqttPublishCount; + info[F("Failed")] = mqttFailedCount; +} + #if HASP_USE_CONFIG > 0 bool mqttGetConfig(const JsonObject& settings) { diff --git a/src/sys/net/hasp_ethernet_esp32.cpp b/src/sys/net/hasp_ethernet_esp32.cpp index a0415e71..87947064 100644 --- a/src/sys/net/hasp_ethernet_esp32.cpp +++ b/src/sys/net/hasp_ethernet_esp32.cpp @@ -70,4 +70,25 @@ void ethernet_get_statusupdate(char* buffer, size_t len) eth_connected ? F("on") : F("off"), ETH.linkSpeed(), ETH.localIP().toString().c_str()); } +void ethernet_get_info(JsonDocument& doc) +{ + char size_buf[32]; + String buffer((char*)0); + buffer.reserve(64); + + JsonObject info = doc.createNestedObject(F("Ethernet")); + + buffer = ETH.linkSpeed(); + buffer += F(" Mbps"); + if(ETH.fullDuplex()) { + buffer += F(" FULL_DUPLEX"); + } + + info[F("Link Speed")] = buffer; + info[F("IP Address")] = ETH.localIP().toString(); + info[F("Gateway")] = ETH.gatewayIP().toString(); + info[F("DNS Server")] = ETH.dnsIP().toString(); + info[F("MAC Address")] = ETH.macAddress(); +} + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_ethernet_esp32.h b/src/sys/net/hasp_ethernet_esp32.h index 8a25a147..4d8463c4 100644 --- a/src/sys/net/hasp_ethernet_esp32.h +++ b/src/sys/net/hasp_ethernet_esp32.h @@ -4,6 +4,8 @@ #ifndef HASP_ETHERNET_ESP32_H #define HASP_ETHERNET_ESP32_H +#include "ArduinoJson.h" + static bool eth_connected = false; void ethernetSetup(); @@ -13,4 +15,6 @@ bool ethernetEverySecond(); bool ethernetEvery5Seconds(); void ethernet_get_statusupdate(char* buffer, size_t len); +void ethernet_get_info(JsonDocument& doc); + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_ethernet_stm32.h b/src/sys/net/hasp_ethernet_stm32.h index f499069f..8e3d2b96 100644 --- a/src/sys/net/hasp_ethernet_stm32.h +++ b/src/sys/net/hasp_ethernet_stm32.h @@ -4,6 +4,8 @@ #ifndef HASP_ETHERNET_STM32_H #define HASP_ETHERNET_STM32_H +#include "ArduinoJson.h" + static bool eth_connected = false; void ethernetSetup(); @@ -13,4 +15,6 @@ bool ethernetEverySecond(); bool ethernetEvery5Seconds(); void ethernet_get_statusupdate(char* buffer, size_t len); +void ethernet_get_info(JsonDocument& doc); + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_network.cpp b/src/sys/net/hasp_network.cpp index bdbc49c0..272e1ad8 100644 --- a/src/sys/net/hasp_network.cpp +++ b/src/sys/net/hasp_network.cpp @@ -132,4 +132,15 @@ void network_get_statusupdate(char* buffer, size_t len) #endif } +void network_get_info(JsonDocument& doc) +{ +#if HASP_USE_ETHERNET > 0 + ethernet_get_info(doc); +#endif + +#if HASP_USE_WIFI > 0 + wifi_get_info(doc); +#endif +} + #endif \ No newline at end of file diff --git a/src/sys/net/hasp_network.h b/src/sys/net/hasp_network.h index 094d96ad..7df30a99 100644 --- a/src/sys/net/hasp_network.h +++ b/src/sys/net/hasp_network.h @@ -16,6 +16,7 @@ void networkStop(void); /* ===== Getter and Setter Functions ===== */ void network_get_statusupdate(char* buffer, size_t len); +void network_get_info(JsonDocument& doc); /* ===== Read/Write Configuration ===== */ diff --git a/src/sys/net/hasp_wifi.cpp b/src/sys/net/hasp_wifi.cpp index 09269c1c..af74b724 100644 --- a/src/sys/net/hasp_wifi.cpp +++ b/src/sys/net/hasp_wifi.cpp @@ -519,6 +519,51 @@ void wifi_get_statusupdate(char* buffer, size_t len) #endif } +void wifi_get_info(JsonDocument& doc) +{ + String buffer((char*)0); + buffer.reserve(64); + + JsonObject info = doc.createNestedObject(F("Wifi")); + + int8_t rssi = WiFi.RSSI(); + buffer += String(rssi); + buffer += F("dBm ("); + + if(rssi >= -50) { + buffer += F("Excellent)"); + } else if(rssi >= -60) { + buffer += F("Good)"); + } else if(rssi >= -70) { + buffer += F("Fair)"); + } else if(rssi >= -80) { + buffer += F("Weak)"); + } else { + buffer += F("Very Bad)"); + } + + info[F("SSID")] = String(WiFi.SSID()); + info[F("Signal Strength")] = buffer; + +#if defined(STM32F4xx) + byte mac[6]; + WiFi.macAddress(mac); + char macAddress[16]; + snprintf_P(macAddress, sizeof(macAddress), PSTR("%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + httpMessage += F("
IP Address: "); + httpMessage += String(WiFi.localIP()); + httpMessage += F("
Gateway: "); + httpMessage += String(WiFi.gatewayIP()); + httpMessage += F("
MAC Address: "); + httpMessage += String(macAddress); +#else + info[F("IP Address")] = WiFi.localIP().toString(); + info[F("Gateway")] = WiFi.gatewayIP().toString(); + info[F("DNS Server")] = WiFi.dnsIP().toString(); + info[F("MAC Address")] = WiFi.macAddress(); +#endif +} + /* ============ Confiuration =============================================================== */ #if HASP_USE_CONFIG > 0 bool wifiGetConfig(const JsonObject& settings) diff --git a/src/sys/net/hasp_wifi.h b/src/sys/net/hasp_wifi.h index 9854d449..b949bc9a 100644 --- a/src/sys/net/hasp_wifi.h +++ b/src/sys/net/hasp_wifi.h @@ -15,6 +15,8 @@ void wifiStop(void); bool wifiValidateSsid(const char* ssid, const char* pass); void wifi_get_statusupdate(char* buffer, size_t len); +void wifi_get_info(JsonDocument& doc); + #if HASP_USE_CONFIG > 0 bool wifiGetConfig(const JsonObject& settings); bool wifiSetConfig(const JsonObject& settings); diff --git a/src/sys/svc/hasp_http.cpp b/src/sys/svc/hasp_http.cpp index bb6e2fae..e69da978 100644 --- a/src/sys/svc/hasp_http.cpp +++ b/src/sys/svc/hasp_http.cpp @@ -18,6 +18,7 @@ #include "hasp_config.h" #if HASP_USE_HTTP > 0 +#include "sys/net/hasp_network.h" // #ifdef USE_CONFIG_OVERRIDE // #include "user_config_override.h" @@ -200,6 +201,22 @@ static void add_form_button(String& str, const __FlashStringHelper* label, const close_form(str); } +static String getContentType(const String& path) +{ + char buff[sizeof(mime::mimeTable[0].mimeType)]; + // Check all entries but last one for match, return if found + for(size_t i = 0; i < sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1; i++) { + strcpy_P(buff, mime::mimeTable[i].endsWith); + if(path.endsWith(buff)) { + strcpy_P(buff, mime::mimeTable[i].mimeType); + return String(buff); + } + } + // Fall-through and just return default type + strcpy_P(buff, mime::mimeTable[sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1].mimeType); + return String(buff); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void webHandleHaspConfig(); @@ -522,6 +539,49 @@ void webHandleAbout() webSendFooter(); } +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void add_json(String& data, JsonDocument& doc) +{ + char buffer[512]; + size_t len = serializeJson(doc, buffer, sizeof(buffer)); + if(len <= 2) return; // empty document + + buffer[len - 1] = ','; + char* start = buffer + 1; + data += String(start); + doc.clear(); +} + +void webHandleInfoJson() +{ // http://plate01/ + if(!httpIsAuthenticated(F("infojson"))) return; + + String htmldata((char*)0); + htmldata.reserve(HTTP_PAGE_SIZE); + DynamicJsonDocument doc(512); + + htmldata = "{"; + + hasp_get_info(doc); + add_json(htmldata, doc); + +#if HASP_USE_MQTT > 0 + mqtt_get_info(doc); + add_json(htmldata, doc); +#endif + + network_get_info(doc); + add_json(htmldata, doc); + + // haspDevice.get_info(doc); + // add_json(htmldata, doc); + + htmldata[htmldata.length() - 1] = '}'; // Replace last comma with a bracket + String path = F(".json"); + webServer.send(200, getContentType(path), htmldata); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void webHandleInfo() { // http://plate01/ @@ -730,52 +790,6 @@ void webHandleInfo() webSendFooter(); } -// String getContentType(String filename) -// { -// if(webServer.hasArg(F("download"))) { -// return F("application/octet-stream"); -// } else if(filename.endsWith(F(".htm")) || filename.endsWith(F(".html"))) { -// return F("text/html"); -// } else if(filename.endsWith(F(".css"))) { -// return F("text/css"); -// } else if(filename.endsWith(F(".js"))) { -// return F("application/javascript"); -// } else if(filename.endsWith(F(".png"))) { -// return F("image/png"); -// } else if(filename.endsWith(F(".gif"))) { -// return F("image/gif"); -// } else if(filename.endsWith(F(".jpg"))) { -// return F("image/jpeg"); -// } else if(filename.endsWith(F(".ico"))) { -// return F("image/x-icon"); -// } else if(filename.endsWith(F(".xml"))) { -// return F("text/xml"); -// } else if(filename.endsWith(F(".pdf"))) { -// return F("application/x-pdf"); -// } else if(filename.endsWith(F(".zip"))) { -// return F("application/x-zip"); -// } else if(filename.endsWith(F(".gz"))) { -// return F("application/x-gzip"); -// } -// return F("text/plain"); -// } - -static String getContentType(const String& path) -{ - char buff[sizeof(mime::mimeTable[0].mimeType)]; - // Check all entries but last one for match, return if found - for(size_t i = 0; i < sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1; i++) { - strcpy_P(buff, mime::mimeTable[i].endsWith); - if(path.endsWith(buff)) { - strcpy_P(buff, mime::mimeTable[i].mimeType); - return String(buff); - } - } - // Fall-through and just return default type - strcpy_P(buff, mime::mimeTable[sizeof(mime::mimeTable) / sizeof(mime::mimeTable[0]) - 1].mimeType); - return String(buff); -} - /* String urldecode(String str) { String encodedString = ""; @@ -2108,6 +2122,7 @@ void httpSetup() webServer.on(F("/"), webHandleRoot); webServer.on(F("/info"), webHandleInfo); + webServer.on(F("/infojson"), webHandleInfoJson); webServer.on(F("/screenshot"), webHandleScreenshot); webServer.on(F("/firmware"), webHandleFirmware); webServer.on(F("/reboot"), httpHandleReboot);