[prometheus] Add prometheus datetime metrics (#9941)

This commit is contained in:
Jordan Zucker authored and GitHub committed 2026-10-27 13:09:01 -10:00
1 parent d552396650
commit 205ef9407f
3 files changed
+153

No files matched your search

@@ -1,4 +1,5 @@
#include "prometheus_handler.h"
#include <cinttypes>
#if defined(USE_NETWORK) && !defined(USE_HOST)
#include "esphome/core/application.h"
@@ -100,6 +101,24 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) {
this->valve_row_(stream, obj, area, node, friendly_name);
#endif
#ifdef USE_DATETIME_DATE
this->date_type_(stream);
for (auto *obj : App.get_dates())
this->date_row_(stream, obj, area, node, friendly_name);
#endif
#ifdef USE_DATETIME_TIME
this->time_type_(stream);
for (auto *obj : App.get_times())
this->time_row_(stream, obj, area, node, friendly_name);
#endif
#ifdef USE_DATETIME_DATETIME
this->datetime_type_(stream);
for (auto *obj : App.get_datetimes())
this->datetime_row_(stream, obj, area, node, friendly_name);
#endif
#ifdef USE_CLIMATE
this->climate_type_(stream);
for (auto *obj : App.get_climates())
@@ -1096,6 +1115,78 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima
}
#endif
#if defined(USE_DATETIME_DATE) || defined(USE_DATETIME_DATETIME) || defined(USE_DATETIME_TIME)
void PrometheusHandler::date_time_rows_(AsyncResponseStream *stream, ProgmemStr failed_metric, ProgmemStr value_metric,
EntityBase *obj, int64_t value_ms, std::string &area, std::string &node,
std::string &friendly_name) {
if (obj->is_internal() && !this->include_internal_)
return;
this->print_metric_labels_(stream, failed_metric, obj, area, node, friendly_name);
if (!obj->has_state()) {
stream->print(ESPHOME_F("\"} 1\n"));
return;
}
stream->print(ESPHOME_F("\"} 0\n"));
this->print_metric_labels_(stream, value_metric, obj, area, node, friendly_name);
// Date and datetime are ms since the epoch (what Grafana expects), time is ms since midnight
char buf[32];
snprintf(buf, sizeof(buf), "\"} %" PRId64 "\n", value_ms);
stream->print(buf);
}
#endif
#ifdef USE_DATETIME_DATE
void PrometheusHandler::date_type_(AsyncResponseStream *stream) {
stream->print(ESPHOME_F("#TYPE esphome_date_value gauge\n"));
stream->print(ESPHOME_F("#TYPE esphome_date_failed gauge\n"));
}
void PrometheusHandler::date_row_(AsyncResponseStream *stream, datetime::DateEntity *obj, std::string &area,
std::string &node, std::string &friendly_name) {
int64_t value_ms = 0;
if (obj->has_state()) {
// DateEntity::state_as_esptime() leaves the time fields unset, so build midnight here
ESPTime t{};
t.year = obj->year;
t.month = obj->month;
t.day_of_month = obj->day;
t.recalc_timestamp_local();
value_ms = static_cast<int64_t>(t.timestamp) * 1000;
}
this->date_time_rows_(stream, ESPHOME_F("esphome_date_failed"), ESPHOME_F("esphome_date_value"), obj, value_ms, area,
node, friendly_name);
}
#endif
#ifdef USE_DATETIME_TIME
void PrometheusHandler::time_type_(AsyncResponseStream *stream) {
stream->print(ESPHOME_F("#TYPE esphome_time_value gauge\n"));
stream->print(ESPHOME_F("#TYPE esphome_time_failed gauge\n"));
}
void PrometheusHandler::time_row_(AsyncResponseStream *stream, datetime::TimeEntity *obj, std::string &area,
std::string &node, std::string &friendly_name) {
int64_t value_ms = 0;
if (obj->has_state())
value_ms = (static_cast<int64_t>(obj->hour) * 3600 + obj->minute * 60 + obj->second) * 1000;
this->date_time_rows_(stream, ESPHOME_F("esphome_time_failed"), ESPHOME_F("esphome_time_value"), obj, value_ms, area,
node, friendly_name);
}
#endif
#ifdef USE_DATETIME_DATETIME
void PrometheusHandler::datetime_type_(AsyncResponseStream *stream) {
stream->print(ESPHOME_F("#TYPE esphome_datetime_value gauge\n"));
stream->print(ESPHOME_F("#TYPE esphome_datetime_failed gauge\n"));
}
void PrometheusHandler::datetime_row_(AsyncResponseStream *stream, datetime::DateTimeEntity *obj, std::string &area,
std::string &node, std::string &friendly_name) {
int64_t value_ms = 0;
if (obj->has_state())
value_ms = static_cast<int64_t>(obj->state_as_esptime().timestamp) * 1000;
this->date_time_rows_(stream, ESPHOME_F("esphome_datetime_failed"), ESPHOME_F("esphome_datetime_value"), obj,
value_ms, area, node, friendly_name);
}
#endif
} // namespace esphome::prometheus
#endif
@@ -193,6 +193,36 @@ class PrometheusHandler final : public AsyncWebHandler, public Component {
std::string &friendly_name);
#endif
#if defined(USE_DATETIME_DATE) || defined(USE_DATETIME_DATETIME) || defined(USE_DATETIME_TIME)
/// Print the failed row and, when the entity has a state, the value row in milliseconds
void date_time_rows_(AsyncResponseStream *stream, ProgmemStr failed_metric, ProgmemStr value_metric, EntityBase *obj,
int64_t value_ms, std::string &area, std::string &node, std::string &friendly_name);
#endif
#ifdef USE_DATETIME_DATE
/// Return the type for prometheus
void date_type_(AsyncResponseStream *stream);
/// Return the date state as prometheus data point
void date_row_(AsyncResponseStream *stream, datetime::DateEntity *obj, std::string &area, std::string &node,
std::string &friendly_name);
#endif
#ifdef USE_DATETIME_TIME
/// Return the type for prometheus
void time_type_(AsyncResponseStream *stream);
/// Return the time state as prometheus data point
void time_row_(AsyncResponseStream *stream, datetime::TimeEntity *obj, std::string &area, std::string &node,
std::string &friendly_name);
#endif
#ifdef USE_DATETIME_DATETIME
/// Return the type for prometheus
void datetime_type_(AsyncResponseStream *stream);
/// Return the datetime state as prometheus data point
void datetime_row_(AsyncResponseStream *stream, datetime::DateTimeEntity *obj, std::string &area, std::string &node,
std::string &friendly_name);
#endif
#ifdef USE_CLIMATE
/// Return the type for prometheus
void climate_type_(AsyncResponseStream *stream);
+32
View File
@@ -182,6 +182,29 @@ climate:
name: LG Climate
transmitter_id: xmitr
datetime:
- platform: template
name: Date
id: test_date
type: date
initial_value: "2000-1-2"
set_action:
- logger.log: "set_value"
- platform: template
name: Time
id: test_time
type: time
initial_value: "12:34:56am"
set_action:
- logger.log: "set_value"
- platform: template
name: DateTime
id: test_datetime
type: datetime
initial_value: "2000-1-2 12:34:56"
set_action:
- logger.log: "set_value"
prometheus:
include_internal: true
relabel:
@@ -191,3 +214,12 @@ prometheus:
template_text_sensor1:
id: hello_text
name: Text Substitution
test_date:
id: prometheus_date
name: Prometheus Date
test_time:
id: prometheus_time
name: Prometheus Time
test_datetime:
id: prometheus_datetime
name: Prometheus DateTime