From 52639a0a7cf8afa5df9a12f07c4f75add33f3284 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 Jul 2020 12:07:05 +0200 Subject: [PATCH] Cleanup web server prometheus integration (#1192) --- esphome/components/prometheus/__init__.py | 22 +++ .../prometheus_handler.cpp} | 144 +++++++----------- .../prometheus_handler.h} | 32 +++- esphome/components/web_server/__init__.py | 5 +- esphome/components/web_server/web_server.cpp | 12 -- esphome/components/web_server/web_server.h | 7 - esphome/const.py | 1 - script/clang-tidy | 6 +- tests/test1.yaml | 1 - tests/test3.yaml | 1 - 10 files changed, 106 insertions(+), 125 deletions(-) create mode 100644 esphome/components/prometheus/__init__.py rename esphome/components/{web_server/web_server_prometheus.cpp => prometheus/prometheus_handler.cpp} (69%) rename esphome/components/{web_server/web_server_prometheus.h => prometheus/prometheus_handler.h} (74%) diff --git a/esphome/components/prometheus/__init__.py b/esphome/components/prometheus/__init__.py new file mode 100644 index 0000000000..d015af9f78 --- /dev/null +++ b/esphome/components/prometheus/__init__.py @@ -0,0 +1,22 @@ +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import CONF_ID +from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID +from esphome.components import web_server_base + +AUTO_LOAD = ['web_server_base'] + +prometheus_ns = cg.esphome_ns.namespace('prometheus') +PrometheusHandler = prometheus_ns.class_('PrometheusHandler', cg.Component) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(PrometheusHandler), + cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(web_server_base.WebServerBase), +}).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + paren = yield cg.get_variable(config[CONF_WEB_SERVER_BASE_ID]) + + var = cg.new_Pvariable(config[CONF_ID], paren) + yield cg.register_component(var, config) diff --git a/esphome/components/web_server/web_server_prometheus.cpp b/esphome/components/prometheus/prometheus_handler.cpp similarity index 69% rename from esphome/components/web_server/web_server_prometheus.cpp rename to esphome/components/prometheus/prometheus_handler.cpp index 4ba75c1673..06a0e39e2c 100644 --- a/esphome/components/web_server/web_server_prometheus.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -1,23 +1,11 @@ -#include "web_server.h" -#include "web_server_prometheus.h" -#include "esphome/core/log.h" +#include "prometheus_handler.h" #include "esphome/core/application.h" -#include "esphome/core/util.h" -#include "esphome/components/json/json_util.h" - -#include "StreamString.h" - -#include - -#ifdef USE_LOGGER -#include -#endif namespace esphome { -namespace web_server { +namespace prometheus { -void WebServerPrometheus::handle_request(AsyncWebServerRequest *request) { - AsyncResponseStream *stream = request->beginResponseStream("text/plain"); +void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) { + AsyncResponseStream *stream = req->beginResponseStream("text/plain"); #ifdef USE_SENSOR this->sensor_type_(stream); @@ -55,16 +43,16 @@ void WebServerPrometheus::handle_request(AsyncWebServerRequest *request) { this->switch_row_(stream, obj); #endif - request->send(stream); + req->send(stream); } // Type-specific implementation #ifdef USE_SENSOR -void WebServerPrometheus::sensor_type_(AsyncResponseStream *stream) { +void PrometheusHandler::sensor_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_sensor_value GAUGE\n")); stream->print(F("#TYPE esphome_sensor_failed GAUGE\n")); } -void WebServerPrometheus::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj) { +void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj) { if (obj->is_internal()) return; if (!isnan(obj->state)) { @@ -97,14 +85,14 @@ void WebServerPrometheus::sensor_row_(AsyncResponseStream *stream, sensor::Senso // Type-specific implementation #ifdef USE_BINARY_SENSOR -void WebServerPrometheus::binary_sensor_type_(AsyncResponseStream *stream) { +void PrometheusHandler::binary_sensor_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_binary_sensor_value GAUGE\n")); stream->print(F("#TYPE esphome_binary_sensor_failed GAUGE\n")); } -void WebServerPrometheus::binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj) { +void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj) { if (obj->is_internal()) return; - if (!isnan(obj->state)) { + if (obj->has_state()) { // We have a valid value, output this value stream->print(F("esphome_binary_sensor_failed{id=\"")); stream->print(obj->get_object_id().c_str()); @@ -131,68 +119,58 @@ void WebServerPrometheus::binary_sensor_row_(AsyncResponseStream *stream, binary #endif #ifdef USE_FAN -void WebServerPrometheus::fan_type_(AsyncResponseStream *stream) { +void PrometheusHandler::fan_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_fan_value GAUGE\n")); stream->print(F("#TYPE esphome_fan_failed GAUGE\n")); stream->print(F("#TYPE esphome_fan_speed GAUGE\n")); stream->print(F("#TYPE esphome_fan_oscillation GAUGE\n")); } -void WebServerPrometheus::fan_row_(AsyncResponseStream *stream, fan::FanState *obj) { +void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::FanState *obj) { if (obj->is_internal()) return; - if (!isnan(obj->state)) { - // We have a valid value, output this value - stream->print(F("esphome_fan_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); - stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); - stream->print(F("\"} 0\n")); - // Data itself - stream->print(F("esphome_fan_value{id=\"")); + stream->print(F("esphome_fan_failed{id=\"")); + stream->print(obj->get_object_id().c_str()); + stream->print(F("\",name=\"")); + stream->print(obj->get_name().c_str()); + stream->print(F("\"} 0\n")); + // Data itself + stream->print(F("esphome_fan_value{id=\"")); + stream->print(obj->get_object_id().c_str()); + stream->print(F("\",name=\"")); + stream->print(obj->get_name().c_str()); + stream->print(F("\"} ")); + stream->print(obj->state); + stream->print('\n'); + // Speed if available + if (obj->get_traits().supports_speed()) { + stream->print(F("esphome_fan_speed{id=\"")); stream->print(obj->get_object_id().c_str()); stream->print(F("\",name=\"")); stream->print(obj->get_name().c_str()); stream->print(F("\"} ")); - stream->print(obj->state); + stream->print(obj->speed); stream->print('\n'); - // Speed if available - if (obj->get_traits().supports_speed()) { - stream->print(F("esphome_fan_speed{id=\"")); - stream->print(obj->get_object_id().c_str()); - stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); - stream->print(F("\"} ")); - stream->print(obj->speed); - stream->print('\n'); - } - // Oscillation if available - if (obj->get_traits().supports_oscillation()) { - stream->print(F("esphome_fan_oscillation{id=\"")); - stream->print(obj->get_object_id().c_str()); - stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); - stream->print(F("\"} ")); - stream->print(obj->oscillating); - stream->print('\n'); - } - } else { - // Invalid state - stream->print(F("esphome_fan_failed{id=\"")); + } + // Oscillation if available + if (obj->get_traits().supports_oscillation()) { + stream->print(F("esphome_fan_oscillation{id=\"")); stream->print(obj->get_object_id().c_str()); stream->print(F("\",name=\"")); stream->print(obj->get_name().c_str()); - stream->print(F("\"} 1\n")); + stream->print(F("\"} ")); + stream->print(obj->oscillating); + stream->print('\n'); } } #endif #ifdef USE_LIGHT -void WebServerPrometheus::light_type_(AsyncResponseStream *stream) { +void PrometheusHandler::light_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_light_state GAUGE\n")); stream->print(F("#TYPE esphome_light_color GAUGE\n")); stream->print(F("#TYPE esphome_light_effect_active GAUGE\n")); } -void WebServerPrometheus::light_row_(AsyncResponseStream *stream, light::LightState *obj) { +void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightState *obj) { if (obj->is_internal()) return; // State @@ -264,11 +242,11 @@ void WebServerPrometheus::light_row_(AsyncResponseStream *stream, light::LightSt #endif #ifdef USE_COVER -void WebServerPrometheus::cover_type_(AsyncResponseStream *stream) { +void PrometheusHandler::cover_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_cover_value GAUGE\n")); stream->print(F("#TYPE esphome_cover_failed GAUGE\n")); } -void WebServerPrometheus::cover_row_(AsyncResponseStream *stream, cover::Cover *obj) { +void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *obj) { if (obj->is_internal()) return; if (!isnan(obj->position)) { @@ -307,38 +285,28 @@ void WebServerPrometheus::cover_row_(AsyncResponseStream *stream, cover::Cover * #endif #ifdef USE_SWITCH -void WebServerPrometheus::switch_type_(AsyncResponseStream *stream) { +void PrometheusHandler::switch_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_switch_value GAUGE\n")); stream->print(F("#TYPE esphome_switch_failed GAUGE\n")); } -void WebServerPrometheus::switch_row_(AsyncResponseStream *stream, switch_::Switch *obj) { +void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch *obj) { if (obj->is_internal()) return; - if (!isnan(obj->state)) { - // We have a valid value, output this value - stream->print(F("esphome_switch_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); - stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); - stream->print(F("\"} 0\n")); - // Data itself - stream->print(F("esphome_switch_value{id=\"")); - stream->print(obj->get_object_id().c_str()); - stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); - stream->print(F("\"} ")); - stream->print(obj->state); - stream->print('\n'); - } else { - // Invalid state - stream->print(F("esphome_switch_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); - stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); - stream->print(F("\"} 1\n")); - } + stream->print(F("esphome_switch_failed{id=\"")); + stream->print(obj->get_object_id().c_str()); + stream->print(F("\",name=\"")); + stream->print(obj->get_name().c_str()); + stream->print(F("\"} 0\n")); + // Data itself + stream->print(F("esphome_switch_value{id=\"")); + stream->print(obj->get_object_id().c_str()); + stream->print(F("\",name=\"")); + stream->print(obj->get_name().c_str()); + stream->print(F("\"} ")); + stream->print(obj->state); + stream->print('\n'); } #endif -} // namespace web_server +} // namespace prometheus } // namespace esphome diff --git a/esphome/components/web_server/web_server_prometheus.h b/esphome/components/prometheus/prometheus_handler.h similarity index 74% rename from esphome/components/web_server/web_server_prometheus.h rename to esphome/components/prometheus/prometheus_handler.h index 93682c1ed5..46ebc400f8 100644 --- a/esphome/components/web_server/web_server_prometheus.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -1,17 +1,31 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/controller.h" #include "esphome/components/web_server_base/web_server_base.h" +#include "esphome/core/controller.h" +#include "esphome/core/component.h" namespace esphome { -namespace web_server { +namespace prometheus { -class WebServerPrometheus { +class PrometheusHandler : public AsyncWebHandler, public Component { public: - WebServerPrometheus(){}; - /// Handle an prometheus metrics request under '/metrics'. - void handle_request(AsyncWebServerRequest *request); + PrometheusHandler(web_server_base::WebServerBase *base) : base_(base) {} + + bool canHandle(AsyncWebServerRequest *request) override { + if (request->method() == HTTP_GET) { + if (request->url() == "/metrics") + return true; + } + + return false; + } + + void handleRequest(AsyncWebServerRequest *req) override; + + void setup() override { + this->base_->init(); + this->base_->add_handler(this); + } protected: #ifdef USE_SENSOR @@ -55,7 +69,9 @@ class WebServerPrometheus { /// Return the switch Values state as prometheus data point void switch_row_(AsyncResponseStream *stream, switch_::Switch *obj); #endif + + web_server_base::WebServerBase *base_; }; -} // namespace web_server +} // namespace prometheus } // namespace esphome diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index be0a180a9d..2f0d179eba 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -4,7 +4,7 @@ from esphome.components import web_server_base from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.const import ( CONF_CSS_INCLUDE, CONF_CSS_URL, CONF_ID, CONF_JS_INCLUDE, CONF_JS_URL, CONF_PORT, - CONF_AUTH, CONF_USERNAME, CONF_PASSWORD, CONF_PROMETHEUS) + CONF_AUTH, CONF_USERNAME, CONF_PASSWORD) from esphome.core import coroutine_with_priority AUTO_LOAD = ['json', 'web_server_base'] @@ -19,7 +19,6 @@ CONFIG_SCHEMA = cv.Schema({ cv.Optional(CONF_CSS_INCLUDE): cv.file_, cv.Optional(CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js"): cv.string, cv.Optional(CONF_JS_INCLUDE): cv.file_, - cv.Optional(CONF_PROMETHEUS, default=False): cv.boolean, cv.Optional(CONF_AUTH): cv.Schema({ cv.Required(CONF_USERNAME): cv.string_strict, cv.Required(CONF_PASSWORD): cv.string_strict, @@ -50,5 +49,3 @@ def to_code(config): cg.add_define('WEBSERVER_JS_INCLUDE') with open(config[CONF_JS_INCLUDE], "r") as myfile: cg.add(var.set_js_include(myfile.read())) - if config[CONF_PROMETHEUS]: - cg.add_define('WEBSERVER_PROMETHEUS') diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 7f0bb0f973..48a47080b2 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -572,11 +572,6 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { if (request->url() == "/") return true; -#ifdef WEBSERVER_PROMETHEUS - if (request->url() == "/metrics") - return true; -#endif - #ifdef WEBSERVER_CSS_INCLUDE if (request->url() == "/0.css") return true; @@ -637,13 +632,6 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { return; } -#ifdef WEBSERVER_PROMETHEUS - if (request->url() == "/metrics") { - this->prometheus.handle_request(request); - return; - } -#endif - #ifdef WEBSERVER_CSS_INCLUDE if (request->url() == "/0.css") { this->handle_css_request(request); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 77fcb1e88c..b3bf2ef7f7 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -3,9 +3,6 @@ #include "esphome/core/component.h" #include "esphome/core/controller.h" #include "esphome/components/web_server_base/web_server_base.h" -#ifdef WEBSERVER_PROMETHEUS -#include "esphome/components/web_server/web_server_prometheus.h" -#endif #include @@ -173,10 +170,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { const char *css_include_{nullptr}; const char *js_url_{nullptr}; const char *js_include_{nullptr}; - -#ifdef WEBSERVER_PROMETHEUS - WebServerPrometheus prometheus; -#endif }; } // namespace web_server diff --git a/esphome/const.py b/esphome/const.py index b65d0977fd..cec93e0d4f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -384,7 +384,6 @@ CONF_POWER_SAVE_MODE = 'power_save_mode' CONF_POWER_SUPPLY = 'power_supply' CONF_PRESSURE = 'pressure' CONF_PRIORITY = 'priority' -CONF_PROMETHEUS = 'prometheus' CONF_PROTOCOL = 'protocol' CONF_PULL_MODE = 'pull_mode' CONF_PULSE_LENGTH = 'pulse_length' diff --git a/script/clang-tidy b/script/clang-tidy index 490e63e0d2..0bd1ef51fa 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -93,11 +93,11 @@ def main(): get_output('clang-tidy-7', '-version') except: print(""" - Oops. It looks like clang-tidy is not installed. - + Oops. It looks like clang-tidy is not installed. + Please check you can run "clang-tidy-7 -version" in your terminal and install clang-tidy (v7) if necessary. - + Note you can also upload your code as a pull request on GitHub and see the CI check output to apply clang-tidy. """) diff --git a/tests/test1.yaml b/tests/test1.yaml index 3738edd1de..d53d113d01 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -166,7 +166,6 @@ web_server: port: 8080 css_url: https://esphome.io/_static/webserver-v1.min.css js_url: https://esphome.io/_static/webserver-v1.min.js - prometheus: true power_supply: id: 'atx_power_supply' diff --git a/tests/test3.yaml b/tests/test3.yaml index 3854a528f4..14cb4a4e47 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -210,7 +210,6 @@ logger: esp8266_store_log_strings_in_flash: false web_server: - prometheus: true deep_sleep: run_duration: 20s