Webserver utilize Component Iterator to not overload eventstream (#3310)

This commit is contained in:
Jesse Hills 2022-03-23 09:45:05 +13:00 committed by GitHub
parent e621b938e3
commit bfbf88b2ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 217 additions and 134 deletions

View File

@ -23,7 +23,7 @@ static const char *const TAG = "api.connection";
static const int ESP32_CAMERA_STOP_STREAM = 5000;
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
: parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
: parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
this->proto_write_buffer_.reserve(64);
#if defined(USE_API_PLAINTEXT)

View File

@ -7,7 +7,6 @@
#include "esphome/components/socket/socket.h"
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "util.h"
#include "list_entities.h"
#include "subscribe_state.h"
#include "user_services.h"

View File

@ -40,8 +40,7 @@ bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->s
#endif
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client)
: ComponentIterator(server), client_(client) {}
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
auto resp = service->encode_list_service_response();
return this->client_->send_list_entities_services_response(resp);

View File

@ -1,8 +1,8 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/component_iterator.h"
#include "esphome/core/defines.h"
#include "util.h"
namespace esphome {
namespace api {
@ -11,7 +11,7 @@ class APIConnection;
class ListEntitiesIterator : public ComponentIterator {
public:
ListEntitiesIterator(APIServer *server, APIConnection *client);
ListEntitiesIterator(APIConnection *client);
#ifdef USE_BINARY_SENSOR
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
#endif
@ -60,5 +60,3 @@ class ListEntitiesIterator : public ComponentIterator {
} // namespace api
} // namespace esphome
#include "api_server.h"

View File

@ -1,5 +1,4 @@
#include "proto.h"
#include "util.h"
#include "esphome/core/log.h"
namespace esphome {

View File

@ -50,8 +50,7 @@ bool InitialStateIterator::on_select(select::Select *select) {
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
#endif
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
: ComponentIterator(server), client_(client) {}
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
} // namespace api
} // namespace esphome

View File

@ -1,9 +1,9 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/component_iterator.h"
#include "esphome/core/controller.h"
#include "esphome/core/defines.h"
#include "util.h"
namespace esphome {
namespace api {
@ -12,7 +12,7 @@ class APIConnection;
class InitialStateIterator : public ComponentIterator {
public:
InitialStateIterator(APIServer *server, APIConnection *client);
InitialStateIterator(APIConnection *client);
#ifdef USE_BINARY_SENSOR
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
#endif
@ -55,5 +55,3 @@ class InitialStateIterator : public ComponentIterator {
} // namespace api
} // namespace esphome
#include "api_server.h"

View File

@ -0,0 +1,97 @@
#ifdef USE_ARDUINO
#include "list_entities.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "web_server.h"
namespace esphome {
namespace web_server {
ListEntitiesIterator::ListEntitiesIterator(WebServer *web_server) : web_server_(web_server) {}
#ifdef USE_BINARY_SENSOR
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
this->web_server_->events_.send(
this->web_server_->binary_sensor_json(binary_sensor, binary_sensor->state, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_COVER
bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
this->web_server_->events_.send(this->web_server_->cover_json(cover, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_FAN
bool ListEntitiesIterator::on_fan(fan::Fan *fan) {
this->web_server_->events_.send(this->web_server_->fan_json(fan, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_LIGHT
bool ListEntitiesIterator::on_light(light::LightState *light) {
this->web_server_->events_.send(this->web_server_->light_json(light, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_SENSOR
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
this->web_server_->events_.send(this->web_server_->sensor_json(sensor, sensor->state, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_SWITCH
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
this->web_server_->events_.send(this->web_server_->switch_json(a_switch, a_switch->state, DETAIL_ALL).c_str(),
"state");
return true;
}
#endif
#ifdef USE_BUTTON
bool ListEntitiesIterator::on_button(button::Button *button) {
this->web_server_->events_.send(this->web_server_->button_json(button, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_TEXT_SENSOR
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
this->web_server_->events_.send(
this->web_server_->text_sensor_json(text_sensor, text_sensor->state, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_LOCK
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
this->web_server_->events_.send(this->web_server_->lock_json(a_lock, a_lock->state, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_CLIMATE
bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
this->web_server_->events_.send(this->web_server_->climate_json(climate, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_NUMBER
bool ListEntitiesIterator::on_number(number::Number *number) {
this->web_server_->events_.send(this->web_server_->number_json(number, number->state, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
#ifdef USE_SELECT
bool ListEntitiesIterator::on_select(select::Select *select) {
this->web_server_->events_.send(this->web_server_->select_json(select, select->state, DETAIL_ALL).c_str(), "state");
return true;
}
#endif
} // namespace web_server
} // namespace esphome
#endif // USE_ARDUINO

View File

@ -0,0 +1,60 @@
#pragma once
#ifdef USE_ARDUINO
#include "esphome/core/component.h"
#include "esphome/core/component_iterator.h"
#include "esphome/core/defines.h"
namespace esphome {
namespace web_server {
class WebServer;
class ListEntitiesIterator : public ComponentIterator {
public:
ListEntitiesIterator(WebServer *web_server);
#ifdef USE_BINARY_SENSOR
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
#endif
#ifdef USE_COVER
bool on_cover(cover::Cover *cover) override;
#endif
#ifdef USE_FAN
bool on_fan(fan::Fan *fan) override;
#endif
#ifdef USE_LIGHT
bool on_light(light::LightState *light) override;
#endif
#ifdef USE_SENSOR
bool on_sensor(sensor::Sensor *sensor) override;
#endif
#ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override;
#endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override;
#endif
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif
#ifdef USE_CLIMATE
bool on_climate(climate::Climate *climate) override;
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
protected:
WebServer *web_server_;
};
} // namespace web_server
} // namespace esphome
#endif // USE_ARDUINO

View File

@ -1,6 +1,7 @@
#ifdef USE_ARDUINO
#include "web_server.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/entity_base.h"
@ -17,7 +18,7 @@
#endif
#ifdef USE_LOGGER
#include <esphome/components/logger/logger.h>
#include "esphome/components/logger/logger.h"
#endif
#ifdef USE_FAN
@ -106,87 +107,7 @@ void WebServer::setup() {
}).c_str(),
"ping", millis(), 30000);
#ifdef USE_SENSOR
for (auto *obj : App.get_sensors()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->sensor_json(obj, obj->state, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_SWITCH
for (auto *obj : App.get_switches()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->switch_json(obj, obj->state, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_BUTTON
for (auto *obj : App.get_buttons())
client->send(this->button_json(obj, DETAIL_ALL).c_str(), "state");
#endif
#ifdef USE_BINARY_SENSOR
for (auto *obj : App.get_binary_sensors()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->binary_sensor_json(obj, obj->state, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_FAN
for (auto *obj : App.get_fans()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->fan_json(obj, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_LIGHT
for (auto *obj : App.get_lights()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->light_json(obj, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_TEXT_SENSOR
for (auto *obj : App.get_text_sensors()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->text_sensor_json(obj, obj->state, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_COVER
for (auto *obj : App.get_covers()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->cover_json(obj, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_NUMBER
for (auto *obj : App.get_numbers()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->number_json(obj, obj->state, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_SELECT
for (auto *obj : App.get_selects()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->select_json(obj, obj->state, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_CLIMATE
for (auto *obj : App.get_climates()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->climate_json(obj, DETAIL_ALL).c_str(), "state");
}
#endif
#ifdef USE_LOCK
for (auto *obj : App.get_locks()) {
if (this->include_internal_ || !obj->is_internal())
client->send(this->lock_json(obj, obj->state, DETAIL_ALL).c_str(), "state");
}
#endif
this->entities_iterator_.begin(this->include_internal_);
});
#ifdef USE_LOGGER
@ -203,6 +124,7 @@ void WebServer::setup() {
this->set_interval(10000, [this]() { this->events_.send("", "ping", millis(), 30000); });
}
void WebServer::loop() { this->entities_iterator_.advance(); }
void WebServer::dump_config() {
ESP_LOGCONFIG(TAG, "Web Server:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port());

View File

@ -2,6 +2,8 @@
#ifdef USE_ARDUINO
#include "list_entities.h"
#include "esphome/components/web_server_base/web_server_base.h"
#include "esphome/core/component.h"
#include "esphome/core/controller.h"
@ -32,7 +34,7 @@ enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
*/
class WebServer : public Controller, public Component, public AsyncWebHandler {
public:
WebServer(web_server_base::WebServerBase *base) : base_(base) {}
WebServer(web_server_base::WebServerBase *base) : base_(base), entities_iterator_(ListEntitiesIterator(this)) {}
/** Set the URL to the CSS <link> that's sent to each client. Defaults to
* https://esphome.io/_static/webserver-v1.min.css
@ -76,6 +78,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
// (In most use cases you won't need these)
/// Setup the internal web server and register handlers.
void setup() override;
void loop() override;
void dump_config() override;
@ -217,8 +220,10 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
bool isRequestHandlerTrivial() override;
protected:
friend ListEntitiesIterator;
web_server_base::WebServerBase *base_;
AsyncEventSource events_{"/events"};
ListEntitiesIterator entities_iterator_;
const char *css_url_{nullptr};
const char *css_include_{nullptr};
const char *js_url_{nullptr};

View File

@ -1,16 +1,18 @@
#include "util.h"
#include "api_server.h"
#include "user_services.h"
#include "esphome/core/log.h"
#include "component_iterator.h"
#include "esphome/core/application.h"
namespace esphome {
namespace api {
#ifdef USE_API
#include "esphome/components/api/api_server.h"
#include "esphome/components/api/user_services.h"
#endif
ComponentIterator::ComponentIterator(APIServer *server) : server_(server) {}
void ComponentIterator::begin() {
namespace esphome {
void ComponentIterator::begin(bool include_internal) {
this->state_ = IteratorState::BEGIN;
this->at_ = 0;
this->include_internal_ = include_internal;
}
void ComponentIterator::advance() {
bool advance_platform = false;
@ -32,7 +34,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *binary_sensor = App.get_binary_sensors()[this->at_];
if (binary_sensor->is_internal()) {
if (binary_sensor->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -47,7 +49,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *cover = App.get_covers()[this->at_];
if (cover->is_internal()) {
if (cover->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -62,7 +64,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *fan = App.get_fans()[this->at_];
if (fan->is_internal()) {
if (fan->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -77,7 +79,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *light = App.get_lights()[this->at_];
if (light->is_internal()) {
if (light->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -92,7 +94,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *sensor = App.get_sensors()[this->at_];
if (sensor->is_internal()) {
if (sensor->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -107,7 +109,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *a_switch = App.get_switches()[this->at_];
if (a_switch->is_internal()) {
if (a_switch->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -122,7 +124,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *button = App.get_buttons()[this->at_];
if (button->is_internal()) {
if (button->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -137,7 +139,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *text_sensor = App.get_text_sensors()[this->at_];
if (text_sensor->is_internal()) {
if (text_sensor->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -146,20 +148,22 @@ void ComponentIterator::advance() {
}
break;
#endif
#ifdef USE_API
case IteratorState ::SERVICE:
if (this->at_ >= this->server_->get_user_services().size()) {
if (this->at_ >= api::global_api_server->get_user_services().size()) {
advance_platform = true;
} else {
auto *service = this->server_->get_user_services()[this->at_];
auto *service = api::global_api_server->get_user_services()[this->at_];
success = this->on_service(service);
}
break;
#endif
#ifdef USE_ESP32_CAMERA
case IteratorState::CAMERA:
if (esp32_camera::global_esp32_camera == nullptr) {
advance_platform = true;
} else {
if (esp32_camera::global_esp32_camera->is_internal()) {
if (esp32_camera::global_esp32_camera->is_internal() && !this->include_internal_) {
advance_platform = success = true;
break;
} else {
@ -174,7 +178,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *climate = App.get_climates()[this->at_];
if (climate->is_internal()) {
if (climate->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -189,7 +193,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *number = App.get_numbers()[this->at_];
if (number->is_internal()) {
if (number->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -204,7 +208,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *select = App.get_selects()[this->at_];
if (select->is_internal()) {
if (select->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -219,7 +223,7 @@ void ComponentIterator::advance() {
advance_platform = true;
} else {
auto *a_lock = App.get_locks()[this->at_];
if (a_lock->is_internal()) {
if (a_lock->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
@ -244,10 +248,10 @@ void ComponentIterator::advance() {
}
bool ComponentIterator::on_end() { return true; }
bool ComponentIterator::on_begin() { return true; }
bool ComponentIterator::on_service(UserServiceDescriptor *service) { return true; }
#ifdef USE_API
bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; }
#endif
#ifdef USE_ESP32_CAMERA
bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; }
#endif
} // namespace api
} // namespace esphome

View File

@ -1,23 +1,24 @@
#pragma once
#include "esphome/core/helpers.h"
#include "esphome/core/component.h"
#include "esphome/core/controller.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP32_CAMERA
#include "esphome/components/esp32_camera/esp32_camera.h"
#endif
namespace esphome {
namespace api {
class APIServer;
#ifdef USE_API
namespace api {
class UserServiceDescriptor;
} // namespace api
#endif
class ComponentIterator {
public:
ComponentIterator(APIServer *server);
void begin();
void begin(bool include_internal = false);
void advance();
virtual bool on_begin();
#ifdef USE_BINARY_SENSOR
@ -44,7 +45,9 @@ class ComponentIterator {
#ifdef USE_TEXT_SENSOR
virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
#endif
virtual bool on_service(UserServiceDescriptor *service);
#ifdef USE_API
virtual bool on_service(api::UserServiceDescriptor *service);
#endif
#ifdef USE_ESP32_CAMERA
virtual bool on_camera(esp32_camera::ESP32Camera *camera);
#endif
@ -90,7 +93,9 @@ class ComponentIterator {
#ifdef USE_TEXT_SENSOR
TEXT_SENSOR,
#endif
#ifdef USE_API
SERVICE,
#endif
#ifdef USE_ESP32_CAMERA
CAMERA,
#endif
@ -109,9 +114,7 @@ class ComponentIterator {
MAX,
} state_{IteratorState::NONE};
size_t at_{0};
APIServer *server_;
bool include_internal_{false};
};
} // namespace api
} // namespace esphome