infrapuzzle/esphome/prometheus_client.h

100 lines
3.9 KiB
C++

#pragma once
#include <WiFiClient.h>
#include <ArduinoJson.h>
#include "esphome.h"
// Forward declare the global variables/pointers defined in main.cpp
extern template_::TemplateSensor *local_cellar_temp;
extern template_::TemplateSensor *local_cellar_humi;
extern template_::TemplateSensor *local_cellar_dp;
extern template_::TemplateSensor *local_cellar_out_temp;
extern template_::TemplateSensor *local_cellar_out_humi;
extern template_::TemplateSensor *local_cellar_out_dp;
extern template_::TemplateSensor *local_fan_status;
extern template_::TemplateSensor *local_mold_critical;
extern template_::TemplateSensor *local_fan_would_activate;
extern template_::TemplateSensor *local_mold_threshold;
extern template_::TemplateSensor *local_shelly_online;
extern globals::GlobalsComponent<bool> *prometheus_connected;
inline void fetch_prometheus() {
WiFiClient client;
if (!client.connect("prometheus.haumdaucher.de", 80)) {
prometheus_connected->value() = false;
ESP_LOGW("taupi", "Failed to connect to Prometheus over HTTP");
return;
}
// Send HTTP/1.0 request to avoid chunked transfer encoding!
client.println("GET /api/v1/query?query=%7B__name__%3D~%22taupi_.*%7Cup%22%2Cjob%3D%22taupi-fan%22%7D HTTP/1.0");
client.println("Host: prometheus.haumdaucher.de");
client.println("Authorization: Basic bW9yaXR6OlZhZWo2UXVpZXF1NHZvMGphZVJh");
client.println("Connection: close");
client.println();
// Read headers
bool headers_ended = false;
std::string body = "";
while (client.connected() || client.available()) {
if (client.available()) {
if (!headers_ended) {
String line = client.readStringUntil('\n');
if (line == "\r" || line == "") {
headers_ended = true;
}
} else {
String data = client.readString();
body += data.c_str();
}
}
}
client.stop();
if (body.length() == 0) {
prometheus_connected->value() = false;
ESP_LOGW("taupi", "Empty response body from Prometheus");
return;
}
prometheus_connected->value() = true;
// Parse body using ArduinoJson 7
JsonDocument doc;
DeserializationError error = deserializeJson(doc, body);
if (!error) {
JsonObject root = doc.as<JsonObject>();
JsonObject data = root["data"];
JsonArray result = data["result"];
for (JsonObject item : result) {
JsonObject metric = item["metric"];
std::string name = metric["__name__"].as<std::string>();
JsonArray value = item["value"];
if (value.size() >= 2) {
std::string val_str = value[1].as<std::string>();
float val = atof(val_str.c_str());
if (name == "taupi_temperature_celsius_innen") local_cellar_temp->publish_state(val);
else if (name == "taupi_humidity_percent_innen") local_cellar_humi->publish_state(val);
else if (name == "taupi_dewpoint_celsius_innen") local_cellar_dp->publish_state(val);
else if (name == "taupi_temperature_celsius_aussen") local_cellar_out_temp->publish_state(val);
else if (name == "taupi_humidity_percent_aussen") local_cellar_out_humi->publish_state(val);
else if (name == "taupi_dewpoint_celsius_aussen") local_cellar_out_dp->publish_state(val);
else if (name == "taupi_relay_status") local_fan_status->publish_state(val);
else if (name == "taupi_is_critical") local_mold_critical->publish_state(val);
else if (name == "taupi_would_fan_activate") local_fan_would_activate->publish_state(val);
else if (name == "taupi_critical_humidity_threshold_percent") local_mold_threshold->publish_state(val);
else if (name == "up") {
std::string job = metric["job"].as<std::string>();
if (job == "taupi-fan") {
local_shelly_online->publish_state(val);
}
}
}
}
ESP_LOGD("taupi", "Successfully parsed Taupi metrics from Prometheus");
} else {
ESP_LOGW("taupi", "JSON parsing failed: %s", error.c_str());
}
}