From 3d6dd8ff872eefa436de6e00cf84cadf6870f78a Mon Sep 17 00:00:00 2001 From: lucs7 Date: Wed, 16 Dec 2020 23:39:33 +0100 Subject: [PATCH] sensor data to prometheus metrics --- tasmota/xsns_75_prometheus.ino | 153 +++++++++++++++++++++++++++------ 1 file changed, 129 insertions(+), 24 deletions(-) diff --git a/tasmota/xsns_75_prometheus.ino b/tasmota/xsns_75_prometheus.ino index 503291cd2..7e1cb5398 100644 --- a/tasmota/xsns_75_prometheus.ino +++ b/tasmota/xsns_75_prometheus.ino @@ -22,17 +22,74 @@ * Prometheus support \*********************************************************************************************/ -#define XSNS_75 75 +#define XSNS_75 75 + +const char *UnitfromType(const char *type) +{ + if (strcmp(type, "time") == 0) + { + return "_seconds"; + } + if (strcmp(type, "temperature") == 0 || strcmp(type, "dewpoint") == 0) + { + return "_celsius"; + } + if (strcmp(type, "pressure") == 0) + { + return "_hpa"; + } + if (strcmp(type, "voltage") == 0) + { + return "_volts"; + } + if (strcmp(type, "current") == 0) + { + return "_amperes"; + } + if (strcmp(type, "mass") == 0) + { + return "_grams"; + } + if (strcmp(type, "carbondioxide") == 0) + { + return "_ppm"; + } + if (strcmp(type, "humidity") == 0) + { + return "_percentage"; + } + return ""; +} + +const char *FormatMetricName(const char *metric) +{ + char *formated = (char *)malloc(strlen(metric) + 1); + uint32_t cnt = 0; + for (cnt; cnt < strlen(metric) + 1; cnt++) + { + if (metric[cnt] == ' ') + { //replace space with + formated[cnt] = '_'; + } + else + { + formated[cnt] = tolower(metric[cnt]); + } + } + return formated; +} void HandleMetrics(void) { - if (!HttpCheckPriviledgedAccess()) { return; } + if (!HttpCheckPriviledgedAccess()) + { + return; + } AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Prometheus")); WSContentBegin(200, CT_PLAIN); - char parameter[FLOATSZ]; // Pseudo-metric providing metadata about the running firmware version. @@ -42,22 +99,24 @@ void HandleMetrics(void) WSContentSend_P(PSTR("# TYPE tasmota_boot_count counter\ntasmota_boot_count %d\n"), Settings.bootcount); WSContentSend_P(PSTR("# TYPE tasmota_flash_writes_total counter\ntasmota_flash_writes_total %d\n"), Settings.save_flag); - // Pseudo-metric providing metadata about the WiFi station. WSContentSend_P(PSTR("# TYPE tasmota_wifi_station_info gauge\ntasmota_wifi_station_info{bssid=\"%s\",ssid=\"%s\"} 1\n"), WiFi.BSSIDstr().c_str(), WiFi.SSID().c_str()); // Wi-Fi Signal strength WSContentSend_P(PSTR("# TYPE tasmota_wifi_station_signal_dbm gauge\ntasmota_wifi_station_signal_dbm{mac_address=\"%s\"} %d\n"), WiFi.BSSIDstr().c_str(), WiFi.RSSI()); - if (!isnan(TasmotaGlobal.temperature_celsius)) { + if (!isnan(TasmotaGlobal.temperature_celsius)) + { dtostrfd(TasmotaGlobal.temperature_celsius, Settings.flag2.temperature_resolution, parameter); WSContentSend_P(PSTR("# TYPE tasmotaglobal_temperature_celsius gauge\ntasmotaglobal_temperature_celsius %s\n"), parameter); } - if (TasmotaGlobal.humidity != 0) { + if (TasmotaGlobal.humidity != 0) + { dtostrfd(TasmotaGlobal.humidity, Settings.flag2.humidity_resolution, parameter); WSContentSend_P(PSTR("# TYPE tasmotaglobal_humidity gauge\ntasmotaglobal_humidity %s\n"), parameter); } - if (TasmotaGlobal.pressure_hpa != 0) { + if (TasmotaGlobal.pressure_hpa != 0) + { dtostrfd(TasmotaGlobal.pressure_hpa, Settings.flag2.pressure_resolution, parameter); WSContentSend_P(PSTR("# TYPE tasmotaglobal_pressure_hpa gauge\ntasmotaglobal_pressure_hpa %s\n"), parameter); } @@ -75,24 +134,69 @@ void HandleMetrics(void) WSContentSend_P(PSTR("# TYPE energy_power_kilowatts_total counter\nenergy_power_kilowatts_total %s\n"), parameter); #endif - for (uint32_t device = 0; device < TasmotaGlobal.devices_present; device++) { + for (uint32_t device = 0; device < TasmotaGlobal.devices_present; device++) + { power_t mask = 1 << device; - WSContentSend_P(PSTR("# TYPE relay%d_state gauge\nrelay%d_state %d\n"), device+1, device+1, (TasmotaGlobal.power & mask)); + WSContentSend_P(PSTR("# TYPE relay%d_state gauge\nrelay%d_state %d\n"), device + 1, device + 1, (TasmotaGlobal.power & mask)); } -/* - // Alternative method using the complete sensor JSON data - // For prometheus it may need to be decoded to # TYPE messages ResponseClear(); MqttShowSensor(); - char json[strlen(TasmotaGlobal.mqtt_data) +1]; + char json[strlen(TasmotaGlobal.mqtt_data) + 1]; snprintf_P(json, sizeof(json), TasmotaGlobal.mqtt_data); - - // Do your Prometheus specific processing here. - // Look at function DisplayAnalyzeJson() in file xdrv_13_display.ino as an example how to decode the JSON message - - WSContentSend_P(json); -*/ + String jsonStr = json; + JsonParser parser((char *)jsonStr.c_str()); + JsonParserObject root = parser.getRootObject(); + if (root) + { // did JSON parsing went ok? + for (auto key1 : root) + { + JsonParserToken value1 = key1.getValue(); + if (value1.isObject()) + { + JsonParserObject Object2 = value1.getObject(); + for (auto key2 : Object2) + { + JsonParserToken value2 = key2.getValue(); + if (value2.isObject()) + { + JsonParserObject Object3 = value2.getObject(); + for (auto key3 : Object3) + { + const char *value = key3.getValue().getStr(nullptr); + if (value != nullptr && isdigit(value[0])) + { + const char *sensor = FormatMetricName(key2.getStr()); //cleanup sensor name + const char *type = FormatMetricName(key3.getStr()); //cleanup sensor type + const char *unit = UnitfromType(type); //grab base unit corresponding to type + WSContentSend_P(PSTR("# TYPE tasmota_sensors_%s%s gauge\ntasmota_sensors_%s%s{sensor=\"%s\"} %s\n"), type, unit, type, unit, sensor, value); //build metric as "# TYPE tasmota_sensors_%type%_%unit% gauge\ntasmotasensors_%type%_%unit%{sensor=%sensor%"} %value%"" + } + } + } + else + { + const char *value = value2.getStr(nullptr); + if (value != nullptr && isdigit(value[0])) + { + const char *sensor = FormatMetricName(key1.getStr()); + const char *type = FormatMetricName(key2.getStr()); + const char *unit = UnitfromType(type); + WSContentSend_P(PSTR("# TYPE tasmota_sensors_%s%s gauge\ntasmota_sensors_%s%s{sensor=\"%s\"} %s\n"), type, unit, type, unit, sensor, value); + } + } + } + } + else + { + const char *value = value1.getStr(nullptr); + if (value != nullptr && isdigit(value[0] && strcmp(key1.getStr(), "Time") != 0)) + { //remove false 'time' metric + const char *sensor = FormatMetricName(key1.getStr()); + WSContentSend_P(PSTR("# TYPE tasmota_sensors_%s gauge\ntasmota_sensors{sensor=\"%s\"} %s\n"), sensor, sensor, value); + } + } + } + } WSContentEnd(); } @@ -105,12 +209,13 @@ bool Xsns75(uint8_t function) { bool result = false; - switch (function) { - case FUNC_WEB_ADD_HANDLER: - WebServer_on(PSTR("/metrics"), HandleMetrics); - break; + switch (function) + { + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/metrics"), HandleMetrics); + break; } return result; } -#endif // USE_PROMETHEUS +#endif // USE_PROMETHEUS