diff --git a/esphome/components/web_server/list_entities.cpp b/esphome/components/web_server/list_entities.cpp index 869ed3ea173..07f2ce205a9 100644 --- a/esphome/components/web_server/list_entities.cpp +++ b/esphome/components/web_server/list_entities.cpp @@ -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 diff --git a/esphome/components/web_server/list_entities.h b/esphome/components/web_server/list_entities.h index dc32cbd1ad6..4323c7e3695 100644 --- a/esphome/components/web_server/list_entities.h +++ b/esphome/components/web_server/list_entities.h @@ -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_; diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 4546ead98b2..613f2f854dd 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -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); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index d545fbf95dd..13695e033ec 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -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 void send_state_event_(T *obj, message_generator_t *generator) { + this->send_state_event_(obj, static_cast(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 diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index 456c9be2c9a..b2792d14d3e 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -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_(); diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index c25dd8d609f..c87aca0aade 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -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; }