esphome 2025.2.2__py3-none-any.whl → 2025.3.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. esphome/__main__.py +9 -1
  2. esphome/components/api/api_connection.cpp +426 -70
  3. esphome/components/api/api_connection.h +117 -25
  4. esphome/components/api/api_pb2.cpp +9 -0
  5. esphome/components/api/api_pb2.h +1 -0
  6. esphome/components/api/api_server.cpp +2 -2
  7. esphome/components/api/list_entities.cpp +76 -22
  8. esphome/components/api/list_entities.h +1 -0
  9. esphome/components/api/subscribe_state.h +2 -0
  10. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -0
  11. esphome/components/bmp085/bmp085.cpp +1 -1
  12. esphome/components/chsc6x/__init__.py +2 -0
  13. esphome/components/chsc6x/chsc6x_touchscreen.cpp +47 -0
  14. esphome/components/chsc6x/chsc6x_touchscreen.h +34 -0
  15. esphome/components/chsc6x/touchscreen.py +33 -0
  16. esphome/components/climate/__init__.py +0 -1
  17. esphome/components/cst816/binary_sensor/__init__.py +2 -25
  18. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +3 -14
  19. esphome/components/cst816/touchscreen/cst816_touchscreen.h +0 -4
  20. esphome/components/esp32_ble_beacon/__init__.py +3 -1
  21. esphome/components/esp8266/gpio.py +1 -2
  22. esphome/components/font/__init__.py +185 -185
  23. esphome/components/font/font.cpp +4 -4
  24. esphome/components/font/font.h +1 -0
  25. esphome/components/haier/climate.py +11 -10
  26. esphome/components/hbridge/switch/hbridge_switch.cpp +2 -2
  27. esphome/components/heatpumpir/climate.py +2 -1
  28. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  29. esphome/components/heatpumpir/heatpumpir.h +1 -0
  30. esphome/components/i2c/__init__.py +6 -6
  31. esphome/components/i2c/i2c_bus_esp_idf.cpp +6 -2
  32. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  33. esphome/components/ili9xxx/display.py +1 -0
  34. esphome/components/ili9xxx/ili9xxx_display.h +5 -0
  35. esphome/components/ili9xxx/ili9xxx_init.h +59 -0
  36. esphome/components/ld2450/__init__.py +51 -0
  37. esphome/components/ld2450/binary_sensor.py +47 -0
  38. esphome/components/ld2450/button/__init__.py +45 -0
  39. esphome/components/ld2450/button/reset_button.cpp +9 -0
  40. esphome/components/ld2450/button/reset_button.h +18 -0
  41. esphome/components/ld2450/button/restart_button.cpp +9 -0
  42. esphome/components/ld2450/button/restart_button.h +18 -0
  43. esphome/components/ld2450/ld2450.cpp +876 -0
  44. esphome/components/ld2450/ld2450.h +234 -0
  45. esphome/components/ld2450/number/__init__.py +121 -0
  46. esphome/components/ld2450/number/presence_timeout_number.cpp +12 -0
  47. esphome/components/ld2450/number/presence_timeout_number.h +18 -0
  48. esphome/components/ld2450/number/zone_coordinate_number.cpp +14 -0
  49. esphome/components/ld2450/number/zone_coordinate_number.h +19 -0
  50. esphome/components/ld2450/select/__init__.py +56 -0
  51. esphome/components/ld2450/select/baud_rate_select.cpp +12 -0
  52. esphome/components/ld2450/select/baud_rate_select.h +18 -0
  53. esphome/components/ld2450/select/zone_type_select.cpp +12 -0
  54. esphome/components/ld2450/select/zone_type_select.h +18 -0
  55. esphome/components/ld2450/sensor.py +156 -0
  56. esphome/components/ld2450/switch/__init__.py +45 -0
  57. esphome/components/ld2450/switch/bluetooth_switch.cpp +12 -0
  58. esphome/components/ld2450/switch/bluetooth_switch.h +18 -0
  59. esphome/components/ld2450/switch/multi_target_switch.cpp +12 -0
  60. esphome/components/ld2450/switch/multi_target_switch.h +18 -0
  61. esphome/components/ld2450/text_sensor.py +62 -0
  62. esphome/components/lvgl/defines.py +0 -2
  63. esphome/components/lvgl/font.cpp +1 -1
  64. esphome/components/lvgl/lvgl_esphome.cpp +27 -19
  65. esphome/components/lvgl/widgets/img.py +1 -3
  66. esphome/components/mcp2515/mcp2515.cpp +1 -0
  67. esphome/components/mlx90393/sensor.py +53 -33
  68. esphome/components/mlx90393/sensor_mlx90393.cpp +4 -0
  69. esphome/components/mlx90393/sensor_mlx90393.h +8 -3
  70. esphome/components/mqtt/__init__.py +2 -2
  71. esphome/components/msa3xx/__init__.py +189 -0
  72. esphome/components/msa3xx/binary_sensor.py +40 -0
  73. esphome/components/msa3xx/msa3xx.cpp +417 -0
  74. esphome/components/msa3xx/msa3xx.h +311 -0
  75. esphome/components/msa3xx/sensor.py +42 -0
  76. esphome/components/msa3xx/text_sensor.py +38 -0
  77. esphome/components/nfc/binary_sensor/__init__.py +4 -4
  78. esphome/components/opentherm/binary_sensor/__init__.py +4 -4
  79. esphome/components/opentherm/generate.py +6 -6
  80. esphome/components/opentherm/sensor/__init__.py +5 -6
  81. esphome/components/packages/__init__.py +35 -11
  82. esphome/components/pn532/binary_sensor.py +4 -4
  83. esphome/components/rc522/binary_sensor.py +4 -4
  84. esphome/components/socket/bsd_sockets_impl.cpp +1 -0
  85. esphome/components/socket/lwip_sockets_impl.cpp +1 -0
  86. esphome/components/socket/socket.h +3 -1
  87. esphome/components/ssd1306_base/__init__.py +7 -7
  88. esphome/components/thermostat/climate.py +1 -1
  89. esphome/components/tmp1075/tmp1075.cpp +7 -11
  90. esphome/components/tmp1075/tmp1075.h +1 -2
  91. esphome/components/tormatic/__init__.py +1 -0
  92. esphome/components/tormatic/cover.py +47 -0
  93. esphome/components/tormatic/tormatic_cover.cpp +355 -0
  94. esphome/components/tormatic/tormatic_cover.h +60 -0
  95. esphome/components/tormatic/tormatic_protocol.h +211 -0
  96. esphome/components/touchscreen/binary_sensor/__init__.py +3 -0
  97. esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp +7 -1
  98. esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h +3 -1
  99. esphome/components/touchscreen/touchscreen.cpp +3 -4
  100. esphome/components/udp/udp_component.h +4 -1
  101. esphome/components/web_server/list_entities.cpp +70 -66
  102. esphome/components/web_server/list_entities.h +43 -22
  103. esphome/components/web_server/web_server.cpp +345 -68
  104. esphome/components/web_server/web_server.h +138 -6
  105. esphome/components/web_server_base/__init__.py +1 -1
  106. esphome/components/web_server_idf/__init__.py +2 -0
  107. esphome/components/web_server_idf/web_server_idf.cpp +177 -30
  108. esphome/components/web_server_idf/web_server_idf.h +53 -4
  109. esphome/config_validation.py +23 -125
  110. esphome/const.py +5 -1
  111. esphome/core/config.py +12 -4
  112. esphome/core/defines.h +1 -1
  113. esphome/core/helpers.h +5 -3
  114. esphome/core/time.cpp +1 -0
  115. esphome/cpp_generator.py +3 -3
  116. esphome/dashboard/core.py +30 -21
  117. esphome/dashboard/dns.py +7 -1
  118. esphome/dashboard/entries.py +83 -16
  119. esphome/dashboard/settings.py +0 -4
  120. esphome/dashboard/status/mdns.py +43 -14
  121. esphome/dashboard/status/mqtt.py +22 -9
  122. esphome/dashboard/status/ping.py +54 -10
  123. esphome/dashboard/web_server.py +56 -24
  124. esphome/storage_json.py +4 -0
  125. esphome/wizard.py +13 -17
  126. esphome/writer.py +1 -3
  127. esphome/yaml_util.py +36 -33
  128. esphome/zeroconf.py +9 -21
  129. {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/METADATA +5 -5
  130. {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/RECORD +134 -94
  131. esphome/components/cst816/binary_sensor/cst816_button.h +0 -27
  132. {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/LICENSE +0 -0
  133. {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/WHEEL +0 -0
  134. {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/entry_points.txt +0 -0
  135. {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/top_level.txt +0 -0
@@ -72,8 +72,146 @@ UrlMatch match_url(const std::string &url, bool only_domain = false) {
72
72
  return match;
73
73
  }
74
74
 
75
- WebServer::WebServer(web_server_base::WebServerBase *base)
76
- : base_(base), entities_iterator_(ListEntitiesIterator(this)) {
75
+ #ifdef USE_ARDUINO
76
+ // helper for allowing only unique entries in the queue
77
+ void DeferredUpdateEventSource::deq_push_back_with_dedup_(void *source, message_generator_t *message_generator) {
78
+ DeferredEvent item(source, message_generator);
79
+
80
+ auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(),
81
+ [&item](const DeferredEvent &test) -> bool { return test == item; });
82
+
83
+ if (iter != this->deferred_queue_.end()) {
84
+ (*iter) = item;
85
+ } else {
86
+ this->deferred_queue_.push_back(item);
87
+ }
88
+ }
89
+
90
+ void DeferredUpdateEventSource::process_deferred_queue_() {
91
+ while (!deferred_queue_.empty()) {
92
+ DeferredEvent &de = deferred_queue_.front();
93
+ std::string message = de.message_generator_(web_server_, de.source_);
94
+ if (this->try_send(message.c_str(), "state")) {
95
+ // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
96
+ deferred_queue_.erase(deferred_queue_.begin());
97
+ } else {
98
+ break;
99
+ }
100
+ }
101
+ }
102
+
103
+ void DeferredUpdateEventSource::loop() {
104
+ process_deferred_queue_();
105
+ if (!this->entities_iterator_.completed())
106
+ this->entities_iterator_.advance();
107
+ }
108
+
109
+ void DeferredUpdateEventSource::deferrable_send_state(void *source, const char *event_type,
110
+ message_generator_t *message_generator) {
111
+ // allow all json "details_all" to go through before publishing bare state events, this avoids unnamed entries showing
112
+ // up in the web GUI and reduces event load during initial connect
113
+ if (!entities_iterator_.completed() && 0 != strcmp(event_type, "state_detail_all"))
114
+ return;
115
+
116
+ if (source == nullptr)
117
+ return;
118
+ if (event_type == nullptr)
119
+ return;
120
+ if (message_generator == nullptr)
121
+ return;
122
+
123
+ if (0 != strcmp(event_type, "state_detail_all") && 0 != strcmp(event_type, "state")) {
124
+ ESP_LOGE(TAG, "Can't defer non-state event");
125
+ }
126
+
127
+ if (!deferred_queue_.empty())
128
+ process_deferred_queue_();
129
+ if (!deferred_queue_.empty()) {
130
+ // deferred queue still not empty which means downstream event queue full, no point trying to send first
131
+ deq_push_back_with_dedup_(source, message_generator);
132
+ } else {
133
+ std::string message = message_generator(web_server_, source);
134
+ if (!this->try_send(message.c_str(), "state")) {
135
+ deq_push_back_with_dedup_(source, message_generator);
136
+ }
137
+ }
138
+ }
139
+
140
+ // used for logs plus the initial ping/config
141
+ void DeferredUpdateEventSource::try_send_nodefer(const char *message, const char *event, uint32_t id,
142
+ uint32_t reconnect) {
143
+ this->send(message, event, id, reconnect);
144
+ }
145
+
146
+ void DeferredUpdateEventSourceList::loop() {
147
+ for (DeferredUpdateEventSource *dues : *this) {
148
+ dues->loop();
149
+ }
150
+ }
151
+
152
+ void DeferredUpdateEventSourceList::deferrable_send_state(void *source, const char *event_type,
153
+ message_generator_t *message_generator) {
154
+ for (DeferredUpdateEventSource *dues : *this) {
155
+ dues->deferrable_send_state(source, event_type, message_generator);
156
+ }
157
+ }
158
+
159
+ void DeferredUpdateEventSourceList::try_send_nodefer(const char *message, const char *event, uint32_t id,
160
+ uint32_t reconnect) {
161
+ for (DeferredUpdateEventSource *dues : *this) {
162
+ dues->try_send_nodefer(message, event, id, reconnect);
163
+ }
164
+ }
165
+
166
+ void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServerRequest *request) {
167
+ DeferredUpdateEventSource *es = new DeferredUpdateEventSource(ws, "/events");
168
+ this->push_back(es);
169
+
170
+ es->onConnect([this, ws, es](AsyncEventSourceClient *client) {
171
+ ws->defer([this, ws, es]() { this->on_client_connect_(ws, es); });
172
+ });
173
+
174
+ es->onDisconnect([this, ws](AsyncEventSource *source, AsyncEventSourceClient *client) {
175
+ ws->defer([this, source]() { this->on_client_disconnect_((DeferredUpdateEventSource *) source); });
176
+ });
177
+
178
+ es->handleRequest(request);
179
+ }
180
+
181
+ void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUpdateEventSource *source) {
182
+ // Configure reconnect timeout and send config
183
+ // this should always go through since the AsyncEventSourceClient event queue is empty on connect
184
+ std::string message = ws->get_config_json();
185
+ source->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
186
+
187
+ for (auto &group : ws->sorting_groups_) {
188
+ message = json::build_json([group](JsonObject root) {
189
+ root["name"] = group.second.name;
190
+ root["sorting_weight"] = group.second.weight;
191
+ });
192
+
193
+ // up to 31 groups should be able to be queued initially without defer
194
+ source->try_send_nodefer(message.c_str(), "sorting_group");
195
+ }
196
+
197
+ source->entities_iterator_.begin(ws->include_internal_);
198
+
199
+ // just dump them all up-front and take advantage of the deferred queue
200
+ // on second thought that takes too long, but leaving the commented code here for debug purposes
201
+ // while(!source->entities_iterator_.completed()) {
202
+ // source->entities_iterator_.advance();
203
+ //}
204
+ }
205
+
206
+ void DeferredUpdateEventSourceList::on_client_disconnect_(DeferredUpdateEventSource *source) {
207
+ // This method was called via WebServer->defer() and is no longer executing in the
208
+ // context of the network callback. The object is now dead and can be safely deleted.
209
+ this->remove(source);
210
+ delete source; // NOLINT
211
+ }
212
+ #endif
213
+
214
+ WebServer::WebServer(web_server_base::WebServerBase *base) : base_(base) {
77
215
  #ifdef USE_ESP32
78
216
  to_schedule_lock_ = xSemaphoreCreateMutex();
79
217
  #endif
@@ -101,34 +239,27 @@ void WebServer::setup() {
101
239
  this->setup_controller(this->include_internal_);
102
240
  this->base_->init();
103
241
 
104
- this->events_.onConnect([this](AsyncEventSourceClient *client) {
105
- // Configure reconnect timeout and send config
106
- client->send(this->get_config_json().c_str(), "ping", millis(), 30000);
107
-
108
- for (auto &group : this->sorting_groups_) {
109
- client->send(json::build_json([group](JsonObject root) {
110
- root["name"] = group.second.name;
111
- root["sorting_weight"] = group.second.weight;
112
- }).c_str(),
113
- "sorting_group");
114
- }
115
-
116
- this->entities_iterator_.begin(this->include_internal_);
117
- });
118
-
119
242
  #ifdef USE_LOGGER
120
243
  if (logger::global_logger != nullptr && this->expose_log_) {
121
244
  logger::global_logger->add_on_log_callback(
122
- [this](int level, const char *tag, const char *message) { this->events_.send(message, "log", millis()); });
245
+ // logs are not deferred, the memory overhead would be too large
246
+ [this](int level, const char *tag, const char *message) {
247
+ this->events_.try_send_nodefer(message, "log", millis());
248
+ });
123
249
  }
124
250
  #endif
251
+
252
+ #ifdef USE_ESP_IDF
125
253
  this->base_->add_handler(&this->events_);
254
+ #endif
126
255
  this->base_->add_handler(this);
127
256
 
128
257
  if (this->allow_ota_)
129
258
  this->base_->add_ota_handler();
130
259
 
131
- this->set_interval(10000, [this]() { this->events_.send("", "ping", millis(), 30000); });
260
+ // doesn't need defer functionality - if the queue is full, the client JS knows it's alive because it's clearly
261
+ // getting a lot of events
262
+ this->set_interval(10000, [this]() { this->events_.try_send_nodefer("", "ping", millis(), 30000); });
132
263
  }
133
264
  void WebServer::loop() {
134
265
  #ifdef USE_ESP32
@@ -147,7 +278,8 @@ void WebServer::loop() {
147
278
  }
148
279
  }
149
280
  #endif
150
- this->entities_iterator_.advance();
281
+
282
+ this->events_.loop();
151
283
  }
152
284
  void WebServer::dump_config() {
153
285
  ESP_LOGCONFIG(TAG, "Web Server:");
@@ -219,9 +351,9 @@ void WebServer::handle_js_request(AsyncWebServerRequest *request) {
219
351
 
220
352
  #ifdef USE_SENSOR
221
353
  void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
222
- if (this->events_.count() == 0)
354
+ if (this->events_.empty())
223
355
  return;
224
- this->events_.send(this->sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
356
+ this->events_.deferrable_send_state(obj, "state", sensor_state_json_generator);
225
357
  }
226
358
  void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
227
359
  for (sensor::Sensor *obj : App.get_sensors()) {
@@ -240,6 +372,12 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM
240
372
  }
241
373
  request->send(404);
242
374
  }
375
+ std::string WebServer::sensor_state_json_generator(WebServer *web_server, void *source) {
376
+ return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_STATE);
377
+ }
378
+ std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *source) {
379
+ return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_ALL);
380
+ }
243
381
  std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
244
382
  return json::build_json([this, obj, value, start_config](JsonObject root) {
245
383
  std::string state;
@@ -267,9 +405,9 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
267
405
 
268
406
  #ifdef USE_TEXT_SENSOR
269
407
  void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
270
- if (this->events_.count() == 0)
408
+ if (this->events_.empty())
271
409
  return;
272
- this->events_.send(this->text_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
410
+ this->events_.deferrable_send_state(obj, "state", text_sensor_state_json_generator);
273
411
  }
274
412
  void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
275
413
  for (text_sensor::TextSensor *obj : App.get_text_sensors()) {
@@ -288,6 +426,14 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const
288
426
  }
289
427
  request->send(404);
290
428
  }
429
+ std::string WebServer::text_sensor_state_json_generator(WebServer *web_server, void *source) {
430
+ return web_server->text_sensor_json((text_sensor::TextSensor *) (source),
431
+ ((text_sensor::TextSensor *) (source))->state, DETAIL_STATE);
432
+ }
433
+ std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, void *source) {
434
+ return web_server->text_sensor_json((text_sensor::TextSensor *) (source),
435
+ ((text_sensor::TextSensor *) (source))->state, DETAIL_ALL);
436
+ }
291
437
  std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
292
438
  JsonDetail start_config) {
293
439
  return json::build_json([this, obj, value, start_config](JsonObject root) {
@@ -306,9 +452,9 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std:
306
452
 
307
453
  #ifdef USE_SWITCH
308
454
  void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
309
- if (this->events_.count() == 0)
455
+ if (this->events_.empty())
310
456
  return;
311
- this->events_.send(this->switch_json(obj, state, DETAIL_STATE).c_str(), "state");
457
+ this->events_.deferrable_send_state(obj, "state", switch_state_json_generator);
312
458
  }
313
459
  void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
314
460
  for (switch_::Switch *obj : App.get_switches()) {
@@ -339,6 +485,12 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
339
485
  }
340
486
  request->send(404);
341
487
  }
488
+ std::string WebServer::switch_state_json_generator(WebServer *web_server, void *source) {
489
+ return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_STATE);
490
+ }
491
+ std::string WebServer::switch_all_json_generator(WebServer *web_server, void *source) {
492
+ return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_ALL);
493
+ }
342
494
  std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
343
495
  return json::build_json([this, obj, value, start_config](JsonObject root) {
344
496
  set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
@@ -379,6 +531,12 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
379
531
  }
380
532
  request->send(404);
381
533
  }
534
+ std::string WebServer::button_state_json_generator(WebServer *web_server, void *source) {
535
+ return web_server->button_json((button::Button *) (source), DETAIL_STATE);
536
+ }
537
+ std::string WebServer::button_all_json_generator(WebServer *web_server, void *source) {
538
+ return web_server->button_json((button::Button *) (source), DETAIL_ALL);
539
+ }
382
540
  std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
383
541
  return json::build_json([this, obj, start_config](JsonObject root) {
384
542
  set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
@@ -396,9 +554,9 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config)
396
554
 
397
555
  #ifdef USE_BINARY_SENSOR
398
556
  void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
399
- if (this->events_.count() == 0)
557
+ if (this->events_.empty())
400
558
  return;
401
- this->events_.send(this->binary_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
559
+ this->events_.deferrable_send_state(obj, "state", binary_sensor_state_json_generator);
402
560
  }
403
561
  void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
404
562
  for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
@@ -417,6 +575,14 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
417
575
  }
418
576
  request->send(404);
419
577
  }
578
+ std::string WebServer::binary_sensor_state_json_generator(WebServer *web_server, void *source) {
579
+ return web_server->binary_sensor_json((binary_sensor::BinarySensor *) (source),
580
+ ((binary_sensor::BinarySensor *) (source))->state, DETAIL_STATE);
581
+ }
582
+ std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, void *source) {
583
+ return web_server->binary_sensor_json((binary_sensor::BinarySensor *) (source),
584
+ ((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL);
585
+ }
420
586
  std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
421
587
  return json::build_json([this, obj, value, start_config](JsonObject root) {
422
588
  set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
@@ -435,9 +601,9 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool
435
601
 
436
602
  #ifdef USE_FAN
437
603
  void WebServer::on_fan_update(fan::Fan *obj) {
438
- if (this->events_.count() == 0)
604
+ if (this->events_.empty())
439
605
  return;
440
- this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state");
606
+ this->events_.deferrable_send_state(obj, "state", fan_state_json_generator);
441
607
  }
442
608
  void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
443
609
  for (fan::Fan *obj : App.get_fans()) {
@@ -494,6 +660,12 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
494
660
  }
495
661
  request->send(404);
496
662
  }
663
+ std::string WebServer::fan_state_json_generator(WebServer *web_server, void *source) {
664
+ return web_server->fan_json((fan::Fan *) (source), DETAIL_STATE);
665
+ }
666
+ std::string WebServer::fan_all_json_generator(WebServer *web_server, void *source) {
667
+ return web_server->fan_json((fan::Fan *) (source), DETAIL_ALL);
668
+ }
497
669
  std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
498
670
  return json::build_json([this, obj, start_config](JsonObject root) {
499
671
  set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
@@ -519,9 +691,9 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
519
691
 
520
692
  #ifdef USE_LIGHT
521
693
  void WebServer::on_light_update(light::LightState *obj) {
522
- if (this->events_.count() == 0)
694
+ if (this->events_.empty())
523
695
  return;
524
- this->events_.send(this->light_json(obj, DETAIL_STATE).c_str(), "state");
696
+ this->events_.deferrable_send_state(obj, "state", light_state_json_generator);
525
697
  }
526
698
  void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
527
699
  for (light::LightState *obj : App.get_lights()) {
@@ -613,6 +785,12 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
613
785
  }
614
786
  request->send(404);
615
787
  }
788
+ std::string WebServer::light_state_json_generator(WebServer *web_server, void *source) {
789
+ return web_server->light_json((light::LightState *) (source), DETAIL_STATE);
790
+ }
791
+ std::string WebServer::light_all_json_generator(WebServer *web_server, void *source) {
792
+ return web_server->light_json((light::LightState *) (source), DETAIL_ALL);
793
+ }
616
794
  std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
617
795
  return json::build_json([this, obj, start_config](JsonObject root) {
618
796
  set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
@@ -638,9 +816,9 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
638
816
 
639
817
  #ifdef USE_COVER
640
818
  void WebServer::on_cover_update(cover::Cover *obj) {
641
- if (this->events_.count() == 0)
819
+ if (this->events_.empty())
642
820
  return;
643
- this->events_.send(this->cover_json(obj, DETAIL_STATE).c_str(), "state");
821
+ this->events_.deferrable_send_state(obj, "state", cover_state_json_generator);
644
822
  }
645
823
  void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
646
824
  for (cover::Cover *obj : App.get_covers()) {
@@ -698,6 +876,12 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
698
876
  }
699
877
  request->send(404);
700
878
  }
879
+ std::string WebServer::cover_state_json_generator(WebServer *web_server, void *source) {
880
+ return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE);
881
+ }
882
+ std::string WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
883
+ return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE);
884
+ }
701
885
  std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
702
886
  return json::build_json([this, obj, start_config](JsonObject root) {
703
887
  set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
@@ -722,9 +906,9 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
722
906
 
723
907
  #ifdef USE_NUMBER
724
908
  void WebServer::on_number_update(number::Number *obj, float state) {
725
- if (this->events_.count() == 0)
909
+ if (this->events_.empty())
726
910
  return;
727
- this->events_.send(this->number_json(obj, state, DETAIL_STATE).c_str(), "state");
911
+ this->events_.deferrable_send_state(obj, "state", number_state_json_generator);
728
912
  }
729
913
  void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
730
914
  for (auto *obj : App.get_numbers()) {
@@ -760,6 +944,12 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
760
944
  request->send(404);
761
945
  }
762
946
 
947
+ std::string WebServer::number_state_json_generator(WebServer *web_server, void *source) {
948
+ return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_STATE);
949
+ }
950
+ std::string WebServer::number_all_json_generator(WebServer *web_server, void *source) {
951
+ return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_ALL);
952
+ }
763
953
  std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
764
954
  return json::build_json([this, obj, value, start_config](JsonObject root) {
765
955
  set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
@@ -796,9 +986,9 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
796
986
 
797
987
  #ifdef USE_DATETIME_DATE
798
988
  void WebServer::on_date_update(datetime::DateEntity *obj) {
799
- if (this->events_.count() == 0)
989
+ if (this->events_.empty())
800
990
  return;
801
- this->events_.send(this->date_json(obj, DETAIL_STATE).c_str(), "state");
991
+ this->events_.deferrable_send_state(obj, "state", date_state_json_generator);
802
992
  }
803
993
  void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
804
994
  for (auto *obj : App.get_dates()) {
@@ -827,7 +1017,7 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
827
1017
  }
828
1018
 
829
1019
  if (request->hasParam("value")) {
830
- std::string value = request->getParam("value")->value().c_str();
1020
+ std::string value = request->getParam("value")->value().c_str(); // NOLINT
831
1021
  call.set_date(value);
832
1022
  }
833
1023
 
@@ -838,6 +1028,12 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
838
1028
  request->send(404);
839
1029
  }
840
1030
 
1031
+ std::string WebServer::date_state_json_generator(WebServer *web_server, void *source) {
1032
+ return web_server->date_json((datetime::DateEntity *) (source), DETAIL_STATE);
1033
+ }
1034
+ std::string WebServer::date_all_json_generator(WebServer *web_server, void *source) {
1035
+ return web_server->date_json((datetime::DateEntity *) (source), DETAIL_ALL);
1036
+ }
841
1037
  std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
842
1038
  return json::build_json([this, obj, start_config](JsonObject root) {
843
1039
  set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
@@ -858,9 +1054,9 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con
858
1054
 
859
1055
  #ifdef USE_DATETIME_TIME
860
1056
  void WebServer::on_time_update(datetime::TimeEntity *obj) {
861
- if (this->events_.count() == 0)
1057
+ if (this->events_.empty())
862
1058
  return;
863
- this->events_.send(this->time_json(obj, DETAIL_STATE).c_str(), "state");
1059
+ this->events_.deferrable_send_state(obj, "state", time_state_json_generator);
864
1060
  }
865
1061
  void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
866
1062
  for (auto *obj : App.get_times()) {
@@ -889,7 +1085,7 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
889
1085
  }
890
1086
 
891
1087
  if (request->hasParam("value")) {
892
- std::string value = request->getParam("value")->value().c_str();
1088
+ std::string value = request->getParam("value")->value().c_str(); // NOLINT
893
1089
  call.set_time(value);
894
1090
  }
895
1091
 
@@ -899,6 +1095,12 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
899
1095
  }
900
1096
  request->send(404);
901
1097
  }
1098
+ std::string WebServer::time_state_json_generator(WebServer *web_server, void *source) {
1099
+ return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_STATE);
1100
+ }
1101
+ std::string WebServer::time_all_json_generator(WebServer *web_server, void *source) {
1102
+ return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_ALL);
1103
+ }
902
1104
  std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
903
1105
  return json::build_json([this, obj, start_config](JsonObject root) {
904
1106
  set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
@@ -919,9 +1121,9 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con
919
1121
 
920
1122
  #ifdef USE_DATETIME_DATETIME
921
1123
  void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) {
922
- if (this->events_.count() == 0)
1124
+ if (this->events_.empty())
923
1125
  return;
924
- this->events_.send(this->datetime_json(obj, DETAIL_STATE).c_str(), "state");
1126
+ this->events_.deferrable_send_state(obj, "state", datetime_state_json_generator);
925
1127
  }
926
1128
  void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
927
1129
  for (auto *obj : App.get_datetimes()) {
@@ -950,7 +1152,7 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
950
1152
  }
951
1153
 
952
1154
  if (request->hasParam("value")) {
953
- std::string value = request->getParam("value")->value().c_str();
1155
+ std::string value = request->getParam("value")->value().c_str(); // NOLINT
954
1156
  call.set_datetime(value);
955
1157
  }
956
1158
 
@@ -960,6 +1162,12 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
960
1162
  }
961
1163
  request->send(404);
962
1164
  }
1165
+ std::string WebServer::datetime_state_json_generator(WebServer *web_server, void *source) {
1166
+ return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_STATE);
1167
+ }
1168
+ std::string WebServer::datetime_all_json_generator(WebServer *web_server, void *source) {
1169
+ return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_ALL);
1170
+ }
963
1171
  std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) {
964
1172
  return json::build_json([this, obj, start_config](JsonObject root) {
965
1173
  set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
@@ -981,9 +1189,9 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s
981
1189
 
982
1190
  #ifdef USE_TEXT
983
1191
  void WebServer::on_text_update(text::Text *obj, const std::string &state) {
984
- if (this->events_.count() == 0)
1192
+ if (this->events_.empty())
985
1193
  return;
986
- this->events_.send(this->text_json(obj, state, DETAIL_STATE).c_str(), "state");
1194
+ this->events_.deferrable_send_state(obj, "state", text_state_json_generator);
987
1195
  }
988
1196
  void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
989
1197
  for (auto *obj : App.get_texts()) {
@@ -1008,7 +1216,7 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
1008
1216
  auto call = obj->make_call();
1009
1217
  if (request->hasParam("value")) {
1010
1218
  String value = request->getParam("value")->value();
1011
- call.set_value(value.c_str());
1219
+ call.set_value(value.c_str()); // NOLINT
1012
1220
  }
1013
1221
 
1014
1222
  this->defer([call]() mutable { call.perform(); });
@@ -1018,6 +1226,12 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
1018
1226
  request->send(404);
1019
1227
  }
1020
1228
 
1229
+ std::string WebServer::text_state_json_generator(WebServer *web_server, void *source) {
1230
+ return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_STATE);
1231
+ }
1232
+ std::string WebServer::text_all_json_generator(WebServer *web_server, void *source) {
1233
+ return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_ALL);
1234
+ }
1021
1235
  std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
1022
1236
  return json::build_json([this, obj, value, start_config](JsonObject root) {
1023
1237
  set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
@@ -1045,9 +1259,9 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
1045
1259
 
1046
1260
  #ifdef USE_SELECT
1047
1261
  void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
1048
- if (this->events_.count() == 0)
1262
+ if (this->events_.empty())
1049
1263
  return;
1050
- this->events_.send(this->select_json(obj, state, DETAIL_STATE).c_str(), "state");
1264
+ this->events_.deferrable_send_state(obj, "state", select_state_json_generator);
1051
1265
  }
1052
1266
  void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1053
1267
  for (auto *obj : App.get_selects()) {
@@ -1074,7 +1288,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
1074
1288
 
1075
1289
  if (request->hasParam("option")) {
1076
1290
  auto option = request->getParam("option")->value();
1077
- call.set_option(option.c_str()); // NOLINT(clang-diagnostic-deprecated-declarations)
1291
+ call.set_option(option.c_str()); // NOLINT
1078
1292
  }
1079
1293
 
1080
1294
  this->schedule_([call]() mutable { call.perform(); });
@@ -1083,6 +1297,12 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
1083
1297
  }
1084
1298
  request->send(404);
1085
1299
  }
1300
+ std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) {
1301
+ return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_STATE);
1302
+ }
1303
+ std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) {
1304
+ return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL);
1305
+ }
1086
1306
  std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
1087
1307
  return json::build_json([this, obj, value, start_config](JsonObject root) {
1088
1308
  set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
@@ -1107,9 +1327,9 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
1107
1327
 
1108
1328
  #ifdef USE_CLIMATE
1109
1329
  void WebServer::on_climate_update(climate::Climate *obj) {
1110
- if (this->events_.count() == 0)
1330
+ if (this->events_.empty())
1111
1331
  return;
1112
- this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
1332
+ this->events_.deferrable_send_state(obj, "state", climate_state_json_generator);
1113
1333
  }
1114
1334
  void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1115
1335
  for (auto *obj : App.get_climates()) {
@@ -1126,6 +1346,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
1126
1346
  request->send(200, "application/json", data.c_str());
1127
1347
  return;
1128
1348
  }
1349
+
1129
1350
  if (match.method != "set") {
1130
1351
  request->send(404);
1131
1352
  return;
@@ -1135,17 +1356,17 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
1135
1356
 
1136
1357
  if (request->hasParam("mode")) {
1137
1358
  auto mode = request->getParam("mode")->value();
1138
- call.set_mode(mode.c_str());
1359
+ call.set_mode(mode.c_str()); // NOLINT
1139
1360
  }
1140
1361
 
1141
1362
  if (request->hasParam("fan_mode")) {
1142
1363
  auto mode = request->getParam("fan_mode")->value();
1143
- call.set_fan_mode(mode.c_str());
1364
+ call.set_fan_mode(mode.c_str()); // NOLINT
1144
1365
  }
1145
1366
 
1146
1367
  if (request->hasParam("swing_mode")) {
1147
1368
  auto mode = request->getParam("swing_mode")->value();
1148
- call.set_swing_mode(mode.c_str());
1369
+ call.set_swing_mode(mode.c_str()); // NOLINT
1149
1370
  }
1150
1371
 
1151
1372
  if (request->hasParam("target_temperature_high")) {
@@ -1172,6 +1393,12 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
1172
1393
  }
1173
1394
  request->send(404);
1174
1395
  }
1396
+ std::string WebServer::climate_state_json_generator(WebServer *web_server, void *source) {
1397
+ return web_server->climate_json((climate::Climate *) (source), DETAIL_STATE);
1398
+ }
1399
+ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *source) {
1400
+ return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
1401
+ }
1175
1402
  std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
1176
1403
  return json::build_json([this, obj, start_config](JsonObject root) {
1177
1404
  set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
@@ -1268,9 +1495,9 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
1268
1495
 
1269
1496
  #ifdef USE_LOCK
1270
1497
  void WebServer::on_lock_update(lock::Lock *obj) {
1271
- if (this->events_.count() == 0)
1498
+ if (this->events_.empty())
1272
1499
  return;
1273
- this->events_.send(this->lock_json(obj, obj->state, DETAIL_STATE).c_str(), "state");
1500
+ this->events_.deferrable_send_state(obj, "state", lock_state_json_generator);
1274
1501
  }
1275
1502
  void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1276
1503
  for (lock::Lock *obj : App.get_locks()) {
@@ -1301,6 +1528,12 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
1301
1528
  }
1302
1529
  request->send(404);
1303
1530
  }
1531
+ std::string WebServer::lock_state_json_generator(WebServer *web_server, void *source) {
1532
+ return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_STATE);
1533
+ }
1534
+ std::string WebServer::lock_all_json_generator(WebServer *web_server, void *source) {
1535
+ return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_ALL);
1536
+ }
1304
1537
  std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
1305
1538
  return json::build_json([this, obj, value, start_config](JsonObject root) {
1306
1539
  set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
@@ -1319,9 +1552,9 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet
1319
1552
 
1320
1553
  #ifdef USE_VALVE
1321
1554
  void WebServer::on_valve_update(valve::Valve *obj) {
1322
- if (this->events_.count() == 0)
1555
+ if (this->events_.empty())
1323
1556
  return;
1324
- this->events_.send(this->valve_json(obj, DETAIL_STATE).c_str(), "state");
1557
+ this->events_.deferrable_send_state(obj, "state", valve_state_json_generator);
1325
1558
  }
1326
1559
  void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1327
1560
  for (valve::Valve *obj : App.get_valves()) {
@@ -1372,6 +1605,12 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
1372
1605
  }
1373
1606
  request->send(404);
1374
1607
  }
1608
+ std::string WebServer::valve_state_json_generator(WebServer *web_server, void *source) {
1609
+ return web_server->valve_json((valve::Valve *) (source), DETAIL_STATE);
1610
+ }
1611
+ std::string WebServer::valve_all_json_generator(WebServer *web_server, void *source) {
1612
+ return web_server->valve_json((valve::Valve *) (source), DETAIL_ALL);
1613
+ }
1375
1614
  std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
1376
1615
  return json::build_json([this, obj, start_config](JsonObject root) {
1377
1616
  set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
@@ -1394,9 +1633,9 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
1394
1633
 
1395
1634
  #ifdef USE_ALARM_CONTROL_PANEL
1396
1635
  void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
1397
- if (this->events_.count() == 0)
1636
+ if (this->events_.empty())
1398
1637
  return;
1399
- this->events_.send(this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE).c_str(), "state");
1638
+ this->events_.deferrable_send_state(obj, "state", alarm_control_panel_state_json_generator);
1400
1639
  }
1401
1640
  void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1402
1641
  for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) {
@@ -1416,7 +1655,7 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
1416
1655
 
1417
1656
  auto call = obj->make_call();
1418
1657
  if (request->hasParam("code")) {
1419
- call.set_code(request->getParam("code")->value().c_str());
1658
+ call.set_code(request->getParam("code")->value().c_str()); // NOLINT
1420
1659
  }
1421
1660
 
1422
1661
  if (match.method == "disarm") {
@@ -1440,6 +1679,16 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
1440
1679
  }
1441
1680
  request->send(404);
1442
1681
  }
1682
+ std::string WebServer::alarm_control_panel_state_json_generator(WebServer *web_server, void *source) {
1683
+ return web_server->alarm_control_panel_json((alarm_control_panel::AlarmControlPanel *) (source),
1684
+ ((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
1685
+ DETAIL_STATE);
1686
+ }
1687
+ std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_server, void *source) {
1688
+ return web_server->alarm_control_panel_json((alarm_control_panel::AlarmControlPanel *) (source),
1689
+ ((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
1690
+ DETAIL_ALL);
1691
+ }
1443
1692
  std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
1444
1693
  alarm_control_panel::AlarmControlPanelState value,
1445
1694
  JsonDetail start_config) {
@@ -1461,8 +1710,9 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro
1461
1710
 
1462
1711
  #ifdef USE_EVENT
1463
1712
  void WebServer::on_event(event::Event *obj, const std::string &event_type) {
1464
- this->events_.send(this->event_json(obj, event_type, DETAIL_STATE).c_str(), "state");
1713
+ this->events_.deferrable_send_state(obj, "state", event_state_json_generator);
1465
1714
  }
1715
+
1466
1716
  void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1467
1717
  for (event::Event *obj : App.get_events()) {
1468
1718
  if (obj->get_object_id() != match.id)
@@ -1481,6 +1731,14 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa
1481
1731
  }
1482
1732
  request->send(404);
1483
1733
  }
1734
+
1735
+ std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) {
1736
+ return web_server->event_json((event::Event *) (source), *(((event::Event *) (source))->last_event_type),
1737
+ DETAIL_STATE);
1738
+ }
1739
+ std::string WebServer::event_all_json_generator(WebServer *web_server, void *source) {
1740
+ return web_server->event_json((event::Event *) (source), *(((event::Event *) (source))->last_event_type), DETAIL_ALL);
1741
+ }
1484
1742
  std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
1485
1743
  return json::build_json([this, obj, event_type, start_config](JsonObject root) {
1486
1744
  set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
@@ -1506,9 +1764,9 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
1506
1764
 
1507
1765
  #ifdef USE_UPDATE
1508
1766
  void WebServer::on_update(update::UpdateEntity *obj) {
1509
- if (this->events_.count() == 0)
1767
+ if (this->events_.empty())
1510
1768
  return;
1511
- this->events_.send(this->update_json(obj, DETAIL_STATE).c_str(), "state");
1769
+ this->events_.deferrable_send_state(obj, "state", update_state_json_generator);
1512
1770
  }
1513
1771
  void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1514
1772
  for (update::UpdateEntity *obj : App.get_updates()) {
@@ -1537,6 +1795,12 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
1537
1795
  }
1538
1796
  request->send(404);
1539
1797
  }
1798
+ std::string WebServer::update_state_json_generator(WebServer *web_server, void *source) {
1799
+ return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1800
+ }
1801
+ std::string WebServer::update_all_json_generator(WebServer *web_server, void *source) {
1802
+ return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1803
+ }
1540
1804
  std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
1541
1805
  return json::build_json([this, obj, start_config](JsonObject root) {
1542
1806
  set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
@@ -1575,6 +1839,12 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
1575
1839
  if (request->url() == "/")
1576
1840
  return true;
1577
1841
 
1842
+ #ifdef USE_ARDUINO
1843
+ if (request->url() == "/events") {
1844
+ return true;
1845
+ }
1846
+ #endif
1847
+
1578
1848
  #ifdef USE_WEBSERVER_CSS_INCLUDE
1579
1849
  if (request->url() == "/0.css")
1580
1850
  return true;
@@ -1597,7 +1867,7 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
1597
1867
  }
1598
1868
  #endif
1599
1869
 
1600
- UrlMatch match = match_url(request->url().c_str(), true);
1870
+ UrlMatch match = match_url(request->url().c_str(), true); // NOLINT
1601
1871
  if (!match.valid)
1602
1872
  return false;
1603
1873
  #ifdef USE_SENSOR
@@ -1708,6 +1978,13 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
1708
1978
  return;
1709
1979
  }
1710
1980
 
1981
+ #ifdef USE_ARDUINO
1982
+ if (request->url() == "/events") {
1983
+ this->events_.add_new_client(this, request);
1984
+ return;
1985
+ }
1986
+ #endif
1987
+
1711
1988
  #ifdef USE_WEBSERVER_CSS_INCLUDE
1712
1989
  if (request->url() == "/0.css") {
1713
1990
  this->handle_css_request(request);
@@ -1729,7 +2006,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
1729
2006
  }
1730
2007
  #endif
1731
2008
 
1732
- UrlMatch match = match_url(request->url().c_str());
2009
+ UrlMatch match = match_url(request->url().c_str()); // NOLINT
1733
2010
  #ifdef USE_SENSOR
1734
2011
  if (match.domain == "sensor") {
1735
2012
  this->handle_sensor_request(request, match);