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);