[web_server] Pass the listing flag as a bool and share the state event send (#21112)

This commit is contained in:
J. Nick Koston
2026-10-19 07:50:39 -10:00
committed by GitHub
parent dc56b27f33
commit 7cf72c287d
6 changed files with 92 additions and 151 deletions
+27 -24
View File
@@ -14,155 +14,158 @@ ListEntitiesIterator::ListEntitiesIterator(const WebServer *ws, AsyncEventSource
ListEntitiesIterator::ListEntitiesIterator(const WebServer *ws, DeferredUpdateEventSource *es)
: web_server_(ws), events_(es) {}
#endif
void ListEntitiesIterator::send_all_(void *source, message_generator_t *generator) {
this->events_->deferrable_send_state(source, true, generator);
}
#ifdef USE_BINARY_SENSOR
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::binary_sensor_all_json_generator);
this->send_all_(obj, WebServer::binary_sensor_all_json_generator);
return true;
}
#endif
#ifdef USE_COVER
bool ListEntitiesIterator::on_cover(cover::Cover *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::cover_all_json_generator);
this->send_all_(obj, WebServer::cover_all_json_generator);
return true;
}
#endif
#ifdef USE_FAN
bool ListEntitiesIterator::on_fan(fan::Fan *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::fan_all_json_generator);
this->send_all_(obj, WebServer::fan_all_json_generator);
return true;
}
#endif
#ifdef USE_LIGHT
bool ListEntitiesIterator::on_light(light::LightState *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::light_all_json_generator);
this->send_all_(obj, WebServer::light_all_json_generator);
return true;
}
#endif
#ifdef USE_SENSOR
bool ListEntitiesIterator::on_sensor(sensor::Sensor *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::sensor_all_json_generator);
this->send_all_(obj, WebServer::sensor_all_json_generator);
return true;
}
#endif
#ifdef USE_SWITCH
bool ListEntitiesIterator::on_switch(switch_::Switch *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::switch_all_json_generator);
this->send_all_(obj, WebServer::switch_all_json_generator);
return true;
}
#endif
#ifdef USE_BUTTON
bool ListEntitiesIterator::on_button(button::Button *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::button_all_json_generator);
this->send_all_(obj, WebServer::button_all_json_generator);
return true;
}
#endif
#ifdef USE_TEXT_SENSOR
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::text_sensor_all_json_generator);
this->send_all_(obj, WebServer::text_sensor_all_json_generator);
return true;
}
#endif
#ifdef USE_LOCK
bool ListEntitiesIterator::on_lock(lock::Lock *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::lock_all_json_generator);
this->send_all_(obj, WebServer::lock_all_json_generator);
return true;
}
#endif
#ifdef USE_VALVE
bool ListEntitiesIterator::on_valve(valve::Valve *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::valve_all_json_generator);
this->send_all_(obj, WebServer::valve_all_json_generator);
return true;
}
#endif
#ifdef USE_CLIMATE
bool ListEntitiesIterator::on_climate(climate::Climate *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::climate_all_json_generator);
this->send_all_(obj, WebServer::climate_all_json_generator);
return true;
}
#endif
#ifdef USE_NUMBER
bool ListEntitiesIterator::on_number(number::Number *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::number_all_json_generator);
this->send_all_(obj, WebServer::number_all_json_generator);
return true;
}
#endif
#ifdef USE_DATETIME_DATE
bool ListEntitiesIterator::on_date(datetime::DateEntity *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::date_all_json_generator);
this->send_all_(obj, WebServer::date_all_json_generator);
return true;
}
#endif
#ifdef USE_DATETIME_TIME
bool ListEntitiesIterator::on_time(datetime::TimeEntity *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::time_all_json_generator);
this->send_all_(obj, WebServer::time_all_json_generator);
return true;
}
#endif
#ifdef USE_DATETIME_DATETIME
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::datetime_all_json_generator);
this->send_all_(obj, WebServer::datetime_all_json_generator);
return true;
}
#endif
#ifdef USE_TEXT
bool ListEntitiesIterator::on_text(text::Text *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::text_all_json_generator);
this->send_all_(obj, WebServer::text_all_json_generator);
return true;
}
#endif
#ifdef USE_SELECT
bool ListEntitiesIterator::on_select(select::Select *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::select_all_json_generator);
this->send_all_(obj, WebServer::select_all_json_generator);
return true;
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::alarm_control_panel_all_json_generator);
this->send_all_(obj, WebServer::alarm_control_panel_all_json_generator);
return true;
}
#endif
#ifdef USE_WATER_HEATER
bool ListEntitiesIterator::on_water_heater(water_heater::WaterHeater *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::water_heater_all_json_generator);
this->send_all_(obj, WebServer::water_heater_all_json_generator);
return true;
}
#endif
#ifdef USE_INFRARED
bool ListEntitiesIterator::on_infrared(infrared::Infrared *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::infrared_all_json_generator);
this->send_all_(obj, WebServer::infrared_all_json_generator);
return true;
}
#endif
#ifdef USE_RADIO_FREQUENCY
bool ListEntitiesIterator::on_radio_frequency(radio_frequency::RadioFrequency *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::radio_frequency_all_json_generator);
this->send_all_(obj, WebServer::radio_frequency_all_json_generator);
return true;
}
#endif
#ifdef USE_EVENT
bool ListEntitiesIterator::on_event(event::Event *obj) {
// Null event type, since we are just iterating over entities
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::event_all_json_generator);
this->send_all_(obj, WebServer::event_all_json_generator);
return true;
}
#endif
#ifdef USE_UPDATE
bool ListEntitiesIterator::on_update(update::UpdateEntity *obj) {
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::update_all_json_generator);
this->send_all_(obj, WebServer::update_all_json_generator);
return true;
}
#endif
@@ -4,6 +4,10 @@
#ifdef USE_WEBSERVER
#include "esphome/core/component.h"
#include "esphome/core/component_iterator.h"
namespace esphome::json {
class JsonBuilder;
} // namespace esphome::json
namespace esphome::web_server_idf {
#ifdef USE_ESP32
class AsyncEventSource;
@@ -37,6 +41,9 @@ class ListEntitiesIterator final : public ComponentIterator {
// NOLINTEND(bugprone-macro-parentheses)
protected:
// Same type as message_generator_t, which this header cannot include
void send_all_(void *source, void (*generator)(WebServer *, void *, json::JsonBuilder &));
const WebServer *web_server_;
#ifdef USE_ESP32
esphome::web_server_idf::AsyncEventSource *events_;
+31 -105
View File
@@ -278,28 +278,17 @@ void DeferredUpdateEventSource::loop() {
this->entities_iterator_.try_advance(1);
}
void DeferredUpdateEventSource::deferrable_send_state(void *source, const char *event_type,
void DeferredUpdateEventSource::deferrable_send_state(void *source, bool detail_all,
message_generator_t *message_generator) {
// Skip if no connected clients to avoid unnecessary deferred queue processing
if (this->count() == 0)
return;
// allow all json "details_all" to go through before publishing bare state events, this avoids unnamed entries showing
// up in the web GUI and reduces event load during initial connect
if (!entities_iterator_.completed() && 0 != strcmp(event_type, "state_detail_all"))
// Let the detail_all listing finish before bare state events, so the web UI never shows unnamed entries and
// initial connect sends less
if (!detail_all && !this->entities_iterator_.completed())
return;
if (source == nullptr)
return;
if (event_type == nullptr)
return;
if (message_generator == nullptr)
return;
if (0 != strcmp(event_type, "state_detail_all") && 0 != strcmp(event_type, "state")) {
ESP_LOGE(TAG, "Can't defer non-state event");
}
if (!deferred_queue_.empty())
process_deferred_queue_();
if (!deferred_queue_.empty()) {
@@ -332,13 +321,13 @@ bool DeferredUpdateEventSourceList::loop() {
return !this->empty();
}
void DeferredUpdateEventSourceList::deferrable_send_state(void *source, const char *event_type,
void DeferredUpdateEventSourceList::deferrable_send_state(void *source, bool detail_all,
message_generator_t *message_generator) {
// Skip if no event sources (no connected clients) to avoid unnecessary iteration
if (this->empty())
return;
for (DeferredUpdateEventSource *dues : *this) {
dues->deferrable_send_state(source, event_type, message_generator);
dues->deferrable_send_state(source, detail_all, message_generator);
}
}
@@ -711,12 +700,13 @@ static void set_json_icon_state_value(JsonObject root, EntityBase *obj, const ch
return request->arg(ESPHOME_F("detail")) == "all" ? DETAIL_ALL : DETAIL_STATE;
}
#ifdef USE_SENSOR
void WebServer::on_sensor_update(sensor::Sensor *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", sensor_state_json_generator);
void WebServer::send_state_event_(void *source, const EntityBase *entity, message_generator_t *generator) {
if (!entity->is_internal() || this->include_internal_)
this->events_.deferrable_send_state(source, false, generator);
}
#ifdef USE_SENSOR
void WebServer::on_sensor_update(sensor::Sensor *obj) { this->send_state_event_(obj, sensor_state_json_generator); }
void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (sensor::Sensor *obj : App.get_sensors()) {
auto entity_match = match.match_entity(obj);
@@ -758,9 +748,7 @@ void WebServer::sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_
#ifdef USE_TEXT_SENSOR
void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", text_sensor_state_json_generator);
this->send_state_event_(obj, text_sensor_state_json_generator);
}
void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (text_sensor::TextSensor *obj : App.get_text_sensors()) {
@@ -818,11 +806,7 @@ static void execute_switch_action(switch_::Switch *obj, SwitchAction action) {
}
}
void WebServer::on_switch_update(switch_::Switch *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", switch_state_json_generator);
}
void WebServer::on_switch_update(switch_::Switch *obj) { this->send_state_event_(obj, switch_state_json_generator); }
void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (switch_::Switch *obj : App.get_switches()) {
auto entity_match = match.match_entity(obj);
@@ -911,9 +895,7 @@ void WebServer::button_json_(button::Button *obj, JsonDetail start_config, json:
#ifdef USE_BINARY_SENSOR
void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", binary_sensor_state_json_generator);
this->send_state_event_(obj, binary_sensor_state_json_generator);
}
void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
@@ -951,11 +933,7 @@ void WebServer::binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value
#endif
#ifdef USE_FAN
void WebServer::on_fan_update(fan::Fan *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", fan_state_json_generator);
}
void WebServer::on_fan_update(fan::Fan *obj) { this->send_state_event_(obj, fan_state_json_generator); }
void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (fan::Fan *obj : App.get_fans()) {
auto entity_match = match.match_entity(obj);
@@ -1030,11 +1008,7 @@ void WebServer::fan_json_(fan::Fan *obj, JsonDetail start_config, json::JsonBuil
#endif
#ifdef USE_LIGHT
void WebServer::on_light_update(light::LightState *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", light_state_json_generator);
}
void WebServer::on_light_update(light::LightState *obj) { this->send_state_event_(obj, light_state_json_generator); }
void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (light::LightState *obj : App.get_lights()) {
auto entity_match = match.match_entity(obj);
@@ -1109,11 +1083,7 @@ void WebServer::light_json_(light::LightState *obj, JsonDetail start_config, jso
#endif
#ifdef USE_COVER
void WebServer::on_cover_update(cover::Cover *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", cover_state_json_generator);
}
void WebServer::on_cover_update(cover::Cover *obj) { this->send_state_event_(obj, cover_state_json_generator); }
void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (cover::Cover *obj : App.get_covers()) {
auto entity_match = match.match_entity(obj);
@@ -1183,11 +1153,7 @@ void WebServer::cover_json_(cover::Cover *obj, JsonDetail start_config, json::Js
#endif
#ifdef USE_NUMBER
void WebServer::on_number_update(number::Number *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", number_state_json_generator);
}
void WebServer::on_number_update(number::Number *obj) { this->send_state_event_(obj, number_state_json_generator); }
void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (auto *obj : App.get_numbers()) {
auto entity_match = match.match_entity(obj);
@@ -1257,11 +1223,7 @@ void WebServer::number_json_(number::Number *obj, JsonDetail start_config, json:
#endif
#ifdef USE_DATETIME_DATE
void WebServer::on_date_update(datetime::DateEntity *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", date_state_json_generator);
}
void WebServer::on_date_update(datetime::DateEntity *obj) { this->send_state_event_(obj, date_state_json_generator); }
void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (auto *obj : App.get_dates()) {
auto entity_match = match.match_entity(obj);
@@ -1316,11 +1278,7 @@ void WebServer::date_json_(datetime::DateEntity *obj, JsonDetail start_config, j
#endif // USE_DATETIME_DATE
#ifdef USE_DATETIME_TIME
void WebServer::on_time_update(datetime::TimeEntity *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", time_state_json_generator);
}
void WebServer::on_time_update(datetime::TimeEntity *obj) { this->send_state_event_(obj, time_state_json_generator); }
void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (auto *obj : App.get_times()) {
auto entity_match = match.match_entity(obj);
@@ -1375,9 +1333,7 @@ void WebServer::time_json_(datetime::TimeEntity *obj, JsonDetail start_config, j
#ifdef USE_DATETIME_DATETIME
void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", datetime_state_json_generator);
this->send_state_event_(obj, datetime_state_json_generator);
}
void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (auto *obj : App.get_datetimes()) {
@@ -1433,11 +1389,7 @@ void WebServer::datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_c
#endif // USE_DATETIME_DATETIME
#ifdef USE_TEXT
void WebServer::on_text_update(text::Text *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", text_state_json_generator);
}
void WebServer::on_text_update(text::Text *obj) { this->send_state_event_(obj, text_state_json_generator); }
void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (auto *obj : App.get_texts()) {
auto entity_match = match.match_entity(obj);
@@ -1496,11 +1448,7 @@ void WebServer::text_json_(text::Text *obj, const std::string &value, JsonDetail
#endif
#ifdef USE_SELECT
void WebServer::on_select_update(select::Select *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", select_state_json_generator);
}
void WebServer::on_select_update(select::Select *obj) { this->send_state_event_(obj, select_state_json_generator); }
void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (auto *obj : App.get_selects()) {
auto entity_match = match.match_entity(obj);
@@ -1556,11 +1504,7 @@ void WebServer::select_json_(select::Select *obj, StringRef value, JsonDetail st
#endif
#ifdef USE_CLIMATE
void WebServer::on_climate_update(climate::Climate *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", climate_state_json_generator);
}
void WebServer::on_climate_update(climate::Climate *obj) { this->send_state_event_(obj, climate_state_json_generator); }
void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (auto *obj : App.get_climates()) {
auto entity_match = match.match_entity(obj);
@@ -1743,11 +1687,7 @@ static void execute_lock_action(lock::Lock *obj, LockAction action) {
}
}
void WebServer::on_lock_update(lock::Lock *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", lock_state_json_generator);
}
void WebServer::on_lock_update(lock::Lock *obj) { this->send_state_event_(obj, lock_state_json_generator); }
void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (lock::Lock *obj : App.get_locks()) {
auto entity_match = match.match_entity(obj);
@@ -1800,11 +1740,7 @@ void WebServer::lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail st
#endif
#ifdef USE_VALVE
void WebServer::on_valve_update(valve::Valve *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", valve_state_json_generator);
}
void WebServer::on_valve_update(valve::Valve *obj) { this->send_state_event_(obj, valve_state_json_generator); }
void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (valve::Valve *obj : App.get_valves()) {
auto entity_match = match.match_entity(obj);
@@ -1870,9 +1806,7 @@ void WebServer::valve_json_(valve::Valve *obj, JsonDetail start_config, json::Js
#ifdef USE_ALARM_CONTROL_PANEL
void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", alarm_control_panel_state_json_generator);
this->send_state_event_(obj, alarm_control_panel_state_json_generator);
}
void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) {
@@ -1940,9 +1874,7 @@ void WebServer::alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel
#ifdef USE_WATER_HEATER
void WebServer::on_water_heater_update(water_heater::WaterHeater *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", water_heater_state_json_generator);
this->send_state_event_(obj, water_heater_state_json_generator);
}
void WebServer::handle_water_heater_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (water_heater::WaterHeater *obj : App.get_water_heaters()) {
@@ -2235,11 +2167,7 @@ void WebServer::radio_frequency_json_(radio_frequency::RadioFrequency *obj, Json
#endif
#ifdef USE_EVENT
void WebServer::on_event(event::Event *obj) {
if (!this->include_internal_ && obj->is_internal())
return;
this->events_.deferrable_send_state(obj, "state", event_state_json_generator);
}
void WebServer::on_event(event::Event *obj) { this->send_state_event_(obj, event_state_json_generator); }
void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (event::Event *obj : App.get_events()) {
@@ -2292,9 +2220,7 @@ void WebServer::event_json_(event::Event *obj, StringRef event_type, JsonDetail
#endif
#ifdef USE_UPDATE
void WebServer::on_update(update::UpdateEntity *obj) {
this->events_.deferrable_send_state(obj, "state", update_state_json_generator);
}
void WebServer::on_update(update::UpdateEntity *obj) { this->send_state_event_(obj, update_state_json_generator); }
void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) {
for (update::UpdateEntity *obj : App.get_updates()) {
auto entity_match = match.match_entity(obj);
+14 -2
View File
@@ -162,7 +162,10 @@ class DeferredUpdateEventSource final : public AsyncEventSource {
void loop();
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
// detail_all events go out while entities are still being listed; state events wait until listing is done
void deferrable_send_state(void *source, bool detail_all, message_generator_t *message_generator);
// The old event name argument would otherwise convert to true
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator) = delete;
void try_send_nodefer(const char *message, size_t message_len, const char *event = nullptr, uint32_t id = 0,
uint32_t reconnect = 0);
};
@@ -176,7 +179,9 @@ class DeferredUpdateEventSourceList final : public std::list<DeferredUpdateEvent
/// Returns true if there are event sources remaining (including pending cleanup).
bool loop();
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
void deferrable_send_state(void *source, bool detail_all, message_generator_t *message_generator);
// The old event name argument would otherwise convert to true
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator) = delete;
void try_send_nodefer(const char *message, size_t message_len, const char *event = nullptr, uint32_t id = 0,
uint32_t reconnect = 0);
@@ -544,6 +549,13 @@ class WebServer final : public Component, public AsyncWebHandler {
// Declared here so it packs into the padding after include_internal_
bool expose_log_{true};
// Out of line so the per-domain update callbacks stay small
// source stays the concrete entity pointer, since the generators cast it back to that type
template<typename T> void send_state_event_(T *obj, message_generator_t *generator) {
this->send_state_event_(obj, static_cast<const EntityBase *>(obj), generator);
}
void send_state_event_(void *source, const EntityBase *entity, message_generator_t *generator);
void add_sorting_info_(JsonObject &root, EntityBase *entity);
#ifdef USE_LIGHT
@@ -797,14 +797,13 @@ void AsyncEventSource::try_send_nodefer(const char *message, size_t message_len,
}
}
void AsyncEventSource::deferrable_send_state(void *source, const char *event_type,
message_generator_t *message_generator) {
void AsyncEventSource::deferrable_send_state(void *source, bool detail_all, message_generator_t *message_generator) {
// Skip if no connected clients to avoid unnecessary processing
if (this->empty())
return;
for (auto *ses : this->sessions_) {
if (ses->fd_.load() != 0) { // Skip dead sessions
ses->deferrable_send_state(source, event_type, message_generator);
ses->deferrable_send_state(source, detail_all, message_generator);
}
}
}
@@ -1268,24 +1267,13 @@ bool AsyncEventSourceResponse::try_send_nodefer(const char *message, size_t mess
return this->stash_chunk_(prefix, prefix_len, message, message_len, g.total, sent);
}
void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *event_type,
void AsyncEventSourceResponse::deferrable_send_state(void *source, bool detail_all,
message_generator_t *message_generator) {
// allow all json "details_all" to go through before publishing bare state events, this avoids unnamed entries showing
// up in the web GUI and reduces event load during initial connect
if (!this->entities_iterator_.completed() && 0 != strcmp(event_type, "state_detail_all"))
// Let the detail_all listing finish before bare state events, so the web UI never shows unnamed entries and
// initial connect sends less
if (!detail_all && !this->entities_iterator_.completed())
return;
if (source == nullptr)
return;
if (event_type == nullptr)
return;
if (message_generator == nullptr)
return;
if (0 != strcmp(event_type, "state_detail_all") && 0 != strcmp(event_type, "state")) {
ESP_LOGE(TAG, "Can't defer non-state event");
}
drain_tail_();
process_deferred_queue_();
@@ -301,7 +301,10 @@ class AsyncEventSourceResponse {
public:
bool try_send_nodefer(const char *message, size_t message_len, const char *event = nullptr, uint32_t id = 0,
uint32_t reconnect = 0);
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
// detail_all events go out while entities are still being listed; state events wait until listing is done
void deferrable_send_state(void *source, bool detail_all, message_generator_t *message_generator);
// The old event name argument would otherwise convert to true
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator) = delete;
void loop();
protected:
@@ -413,7 +416,9 @@ class AsyncEventSource : public AsyncWebHandler {
void handleRequest(AsyncWebServerRequest *request) override;
void try_send_nodefer(const char *message, size_t message_len, const char *event = nullptr, uint32_t id = 0,
uint32_t reconnect = 0);
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
void deferrable_send_state(void *source, bool detail_all, message_generator_t *message_generator);
// The old event name argument would otherwise convert to true
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator) = delete;
/// Returns true if there are sessions remaining (including pending cleanup).
bool loop();
bool empty() { return this->count() == 0; }