From 6f07421911bd1390cbf776956e3f4c4f45301f6b Mon Sep 17 00:00:00 2001 From: mechanarchy <1166756+mechanarchy@users.noreply.github.com> Date: Tue, 30 Nov 2021 02:52:20 +1100 Subject: [PATCH] Optionally show internal components on the web server (#2627) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Oxan van Leeuwen --- esphome/components/web_server/__init__.py | 3 + esphome/components/web_server/web_server.cpp | 109 ++++++++----------- esphome/components/web_server/web_server.h | 7 ++ esphome/const.py | 1 + esphome/core/controller.cpp | 22 ++-- esphome/core/controller.h | 2 +- tests/test4.yaml | 1 + 7 files changed, 67 insertions(+), 78 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index dc652e0312..d9ff84d501 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -12,6 +12,7 @@ from esphome.const import ( CONF_AUTH, CONF_USERNAME, CONF_PASSWORD, + CONF_INCLUDE_INTERNAL, CONF_OTA, ) from esphome.core import CORE, coroutine_with_priority @@ -42,6 +43,7 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( web_server_base.WebServerBase ), + cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean, cv.Optional(CONF_OTA, default=True): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA) @@ -75,3 +77,4 @@ async def to_code(config): path = CORE.relative_config_path(config[CONF_JS_INCLUDE]) with open(file=path, mode="r", encoding="utf-8") as myfile: cg.add(var.set_js_include(myfile.read())) + cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL])) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 6f47f460af..3f74c2e8a1 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -31,10 +31,10 @@ static const char *const TAG = "web_server"; void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action, const std::function &action_func = nullptr) { - if (obj->is_internal()) - return; stream->print("print(klass.c_str()); + if (obj->is_internal()) + stream->print(" internal"); stream->print("\" id=\""); stream->print(klass.c_str()); stream->print("-"); @@ -83,7 +83,7 @@ void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_ void WebServer::setup() { ESP_LOGCONFIG(TAG, "Setting up web server..."); - this->setup_controller(); + this->setup_controller(this->include_internal_); this->base_->init(); this->events_.onConnect([this](AsyncEventSourceClient *client) { @@ -92,55 +92,55 @@ void WebServer::setup() { #ifdef USE_SENSOR for (auto *obj : App.get_sensors()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->sensor_json(obj, obj->state).c_str(), "state"); #endif #ifdef USE_SWITCH for (auto *obj : App.get_switches()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->switch_json(obj, obj->state).c_str(), "state"); #endif #ifdef USE_BINARY_SENSOR for (auto *obj : App.get_binary_sensors()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->binary_sensor_json(obj, obj->state).c_str(), "state"); #endif #ifdef USE_FAN for (auto *obj : App.get_fans()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->fan_json(obj).c_str(), "state"); #endif #ifdef USE_LIGHT for (auto *obj : App.get_lights()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->light_json(obj).c_str(), "state"); #endif #ifdef USE_TEXT_SENSOR for (auto *obj : App.get_text_sensors()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->text_sensor_json(obj, obj->state).c_str(), "state"); #endif #ifdef USE_COVER for (auto *obj : App.get_covers()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->cover_json(obj).c_str(), "state"); #endif #ifdef USE_NUMBER for (auto *obj : App.get_numbers()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->number_json(obj, obj->state).c_str(), "state"); #endif #ifdef USE_SELECT for (auto *obj : App.get_selects()) - if (!obj->is_internal()) + if (this->include_internal_ || !obj->is_internal()) client->send(this->select_json(obj, obj->state).c_str(), "state"); #endif }); @@ -188,57 +188,66 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { #ifdef USE_SENSOR for (auto *obj : App.get_sensors()) - write_row(stream, obj, "sensor", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "sensor", ""); #endif #ifdef USE_SWITCH for (auto *obj : App.get_switches()) - write_row(stream, obj, "switch", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "switch", ""); #endif #ifdef USE_BINARY_SENSOR for (auto *obj : App.get_binary_sensors()) - write_row(stream, obj, "binary_sensor", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "binary_sensor", ""); #endif #ifdef USE_FAN for (auto *obj : App.get_fans()) - write_row(stream, obj, "fan", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "fan", ""); #endif #ifdef USE_LIGHT for (auto *obj : App.get_lights()) - write_row(stream, obj, "light", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "light", ""); #endif #ifdef USE_TEXT_SENSOR for (auto *obj : App.get_text_sensors()) - write_row(stream, obj, "text_sensor", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "text_sensor", ""); #endif #ifdef USE_COVER for (auto *obj : App.get_covers()) - write_row(stream, obj, "cover", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "cover", ""); #endif #ifdef USE_NUMBER for (auto *obj : App.get_numbers()) - write_row(stream, obj, "number", ""); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "number", ""); #endif #ifdef USE_SELECT for (auto *obj : App.get_selects()) - write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, EntityBase *obj) { - select::Select *select = (select::Select *) obj; - stream.print(""); - }); + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, EntityBase *obj) { + select::Select *select = (select::Select *) obj; + stream.print(""); + }); #endif stream->print(F("

See ESPHome Web API for " @@ -293,8 +302,6 @@ void WebServer::on_sensor_update(sensor::Sensor *obj, float state) { } void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (sensor::Sensor *obj : App.get_sensors()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; std::string data = this->sensor_json(obj, obj->state); @@ -321,8 +328,6 @@ void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s } void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (text_sensor::TextSensor *obj : App.get_text_sensors()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; std::string data = this->text_sensor_json(obj, obj->state); @@ -353,8 +358,6 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value) { } void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (switch_::Switch *obj : App.get_switches()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; @@ -381,8 +384,6 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM #ifdef USE_BINARY_SENSOR void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) { - if (obj->is_internal()) - return; this->events_.send(this->binary_sensor_json(obj, state).c_str(), "state"); } std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value) { @@ -394,8 +395,6 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool } void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; std::string data = this->binary_sensor_json(obj, obj->state); @@ -407,11 +406,7 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con #endif #ifdef USE_FAN -void WebServer::on_fan_update(fan::FanState *obj) { - if (obj->is_internal()) - return; - this->events_.send(this->fan_json(obj).c_str(), "state"); -} +void WebServer::on_fan_update(fan::FanState *obj) { this->events_.send(this->fan_json(obj).c_str(), "state"); } std::string WebServer::fan_json(fan::FanState *obj) { return json::build_json([obj](JsonObject &root) { root["id"] = "fan-" + obj->get_object_id(); @@ -442,8 +437,6 @@ std::string WebServer::fan_json(fan::FanState *obj) { } void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (fan::FanState *obj : App.get_fans()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; @@ -504,15 +497,9 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc #endif #ifdef USE_LIGHT -void WebServer::on_light_update(light::LightState *obj) { - if (obj->is_internal()) - return; - this->events_.send(this->light_json(obj).c_str(), "state"); -} +void WebServer::on_light_update(light::LightState *obj) { this->events_.send(this->light_json(obj).c_str(), "state"); } void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (light::LightState *obj : App.get_lights()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; @@ -579,15 +566,9 @@ std::string WebServer::light_json(light::LightState *obj) { #endif #ifdef USE_COVER -void WebServer::on_cover_update(cover::Cover *obj) { - if (obj->is_internal()) - return; - this->events_.send(this->cover_json(obj).c_str(), "state"); -} +void WebServer::on_cover_update(cover::Cover *obj) { this->events_.send(this->cover_json(obj).c_str(), "state"); } void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (cover::Cover *obj : App.get_covers()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; @@ -646,8 +627,6 @@ void WebServer::on_number_update(number::Number *obj, float state) { } void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_numbers()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; std::string data = this->number_json(obj, obj->state); @@ -673,8 +652,6 @@ void WebServer::on_select_update(select::Select *obj, const std::string &state) } void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_selects()) { - if (obj->is_internal()) - continue; if (obj->get_object_id() != match.id) continue; diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index cdfec51cf1..66fd082d19 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -58,6 +58,12 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { */ void set_js_include(const char *js_include); + /** Determine whether internal components should be displayed on the web server. + * Defaults to false. + * + * @param include_internal Whether internal components should be displayed. + */ + void set_include_internal(bool include_internal) { include_internal_ = include_internal; } /** Set whether or not the webserver should expose the OTA form and handler. * * @param allow_ota. @@ -188,6 +194,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { const char *css_include_{nullptr}; const char *js_url_{nullptr}; const char *js_include_{nullptr}; + bool include_internal_{false}; bool allow_ota_{true}; }; diff --git a/esphome/const.py b/esphome/const.py index f7beee8245..9006145dfe 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -286,6 +286,7 @@ CONF_ILLUMINANCE = "illuminance" CONF_IMPEDANCE = "impedance" CONF_IMPORT_ACTIVE_ENERGY = "import_active_energy" CONF_IMPORT_REACTIVE_ENERGY = "import_reactive_energy" +CONF_INCLUDE_INTERNAL = "include_internal" CONF_INCLUDES = "includes" CONF_INDEX = "index" CONF_INDOOR = "indoor" diff --git a/esphome/core/controller.cpp b/esphome/core/controller.cpp index 1d25be41f2..6d3a76a292 100644 --- a/esphome/core/controller.cpp +++ b/esphome/core/controller.cpp @@ -4,64 +4,64 @@ namespace esphome { -void Controller::setup_controller() { +void Controller::setup_controller(bool include_internal) { #ifdef USE_BINARY_SENSOR for (auto *obj : App.get_binary_sensors()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj](bool state) { this->on_binary_sensor_update(obj, state); }); } #endif #ifdef USE_FAN for (auto *obj : App.get_fans()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj]() { this->on_fan_update(obj); }); } #endif #ifdef USE_LIGHT for (auto *obj : App.get_lights()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_new_remote_values_callback([this, obj]() { this->on_light_update(obj); }); } #endif #ifdef USE_SENSOR for (auto *obj : App.get_sensors()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj](float state) { this->on_sensor_update(obj, state); }); } #endif #ifdef USE_SWITCH for (auto *obj : App.get_switches()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj](bool state) { this->on_switch_update(obj, state); }); } #endif #ifdef USE_COVER for (auto *obj : App.get_covers()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj]() { this->on_cover_update(obj); }); } #endif #ifdef USE_TEXT_SENSOR for (auto *obj : App.get_text_sensors()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj](const std::string &state) { this->on_text_sensor_update(obj, state); }); } #endif #ifdef USE_CLIMATE for (auto *obj : App.get_climates()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj]() { this->on_climate_update(obj); }); } #endif #ifdef USE_NUMBER for (auto *obj : App.get_numbers()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj](float state) { this->on_number_update(obj, state); }); } #endif #ifdef USE_SELECT for (auto *obj : App.get_selects()) { - if (!obj->is_internal()) + if (include_internal || !obj->is_internal()) obj->add_on_state_callback([this, obj](const std::string &state) { this->on_select_update(obj, state); }); } #endif diff --git a/esphome/core/controller.h b/esphome/core/controller.h index 0de8f7ea19..f53924cd23 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -36,7 +36,7 @@ namespace esphome { class Controller { public: - void setup_controller(); + void setup_controller(bool include_internal = false); #ifdef USE_BINARY_SENSOR virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state){}; #endif diff --git a/tests/test4.yaml b/tests/test4.yaml index 938145235a..ee88b422f2 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -49,6 +49,7 @@ web_server: auth: username: admin password: admin + include_internal: true time: - platform: sntp