ESPHome  2025.3.3
web_server.cpp
Go to the documentation of this file.
1 #include "web_server.h"
2 #ifdef USE_WEBSERVER
7 #include "esphome/core/helpers.h"
8 #include "esphome/core/log.h"
9 #include "esphome/core/util.h"
10 
11 #ifdef USE_ARDUINO
12 #include "StreamString.h"
13 #endif
14 
15 #include <cstdlib>
16 
17 #ifdef USE_LIGHT
19 #endif
20 
21 #ifdef USE_LOGGER
23 #endif
24 
25 #ifdef USE_CLIMATE
27 #endif
28 
29 #ifdef USE_WEBSERVER_LOCAL
30 #if USE_WEBSERVER_VERSION == 2
31 #include "server_index_v2.h"
32 #elif USE_WEBSERVER_VERSION == 3
33 #include "server_index_v3.h"
34 #endif
35 #endif
36 
37 namespace esphome {
38 namespace web_server {
39 
40 static const char *const TAG = "web_server";
41 
42 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
43 static const char *const HEADER_PNA_NAME = "Private-Network-Access-Name";
44 static const char *const HEADER_PNA_ID = "Private-Network-Access-ID";
45 static const char *const HEADER_CORS_REQ_PNA = "Access-Control-Request-Private-Network";
46 static const char *const HEADER_CORS_ALLOW_PNA = "Access-Control-Allow-Private-Network";
47 #endif
48 
49 UrlMatch match_url(const std::string &url, bool only_domain = false) {
50  UrlMatch match;
51  match.valid = false;
52  size_t domain_end = url.find('/', 1);
53  if (domain_end == std::string::npos)
54  return match;
55  match.domain = url.substr(1, domain_end - 1);
56  if (only_domain) {
57  match.valid = true;
58  return match;
59  }
60  if (url.length() == domain_end - 1)
61  return match;
62  size_t id_begin = domain_end + 1;
63  size_t id_end = url.find('/', id_begin);
64  match.valid = true;
65  if (id_end == std::string::npos) {
66  match.id = url.substr(id_begin, url.length() - id_begin);
67  return match;
68  }
69  match.id = url.substr(id_begin, id_end - id_begin);
70  size_t method_begin = id_end + 1;
71  match.method = url.substr(method_begin, url.length() - method_begin);
72  return match;
73 }
74 
75 #ifdef USE_ARDUINO
76 // helper for allowing only unique entries in the queue
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 
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 
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())
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 
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 
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 
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 
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 
215 #ifdef USE_ESP32
216  to_schedule_lock_ = xSemaphoreCreateMutex();
217 #endif
218 }
219 
220 #ifdef USE_WEBSERVER_CSS_INCLUDE
221 void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; }
222 #endif
223 #ifdef USE_WEBSERVER_JS_INCLUDE
224 void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; }
225 #endif
226 
228  return json::build_json([this](JsonObject root) {
229  root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
230  root["comment"] = App.get_comment();
231  root["ota"] = this->allow_ota_;
232  root["log"] = this->expose_log_;
233  root["lang"] = "en";
234  });
235 }
236 
238  ESP_LOGCONFIG(TAG, "Setting up web server...");
239  this->setup_controller(this->include_internal_);
240  this->base_->init();
241 
242 #ifdef USE_LOGGER
243  if (logger::global_logger != nullptr && this->expose_log_) {
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  });
249  }
250 #endif
251 
252 #ifdef USE_ESP_IDF
253  this->base_->add_handler(&this->events_);
254 #endif
255  this->base_->add_handler(this);
256 
257  if (this->allow_ota_)
258  this->base_->add_ota_handler();
259 
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); });
263 }
265 #ifdef USE_ESP32
266  if (xSemaphoreTake(this->to_schedule_lock_, 0L)) {
267  std::function<void()> fn;
268  if (!to_schedule_.empty()) {
269  // scheduler execute things out of order which may lead to incorrect state
270  // this->defer(std::move(to_schedule_.front()));
271  // let's execute it directly from the loop
272  fn = std::move(to_schedule_.front());
273  to_schedule_.pop_front();
274  }
275  xSemaphoreGive(this->to_schedule_lock_);
276  if (fn) {
277  fn();
278  }
279  }
280 #endif
281 
282  this->events_.loop();
283 }
285  ESP_LOGCONFIG(TAG, "Web Server:");
286  ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port());
287 }
288 float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; }
289 
290 #ifdef USE_WEBSERVER_LOCAL
291 void WebServer::handle_index_request(AsyncWebServerRequest *request) {
292  AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
293  response->addHeader("Content-Encoding", "gzip");
294  request->send(response);
295 }
296 #elif USE_WEBSERVER_VERSION >= 2
297 void WebServer::handle_index_request(AsyncWebServerRequest *request) {
298  AsyncWebServerResponse *response =
299  request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
300  // No gzip header here because the HTML file is so small
301  request->send(response);
302 }
303 #endif
304 
305 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
306 void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) {
307  AsyncWebServerResponse *response = request->beginResponse(200, "");
308  response->addHeader(HEADER_CORS_ALLOW_PNA, "true");
309  response->addHeader(HEADER_PNA_NAME, App.get_name().c_str());
310  std::string mac = get_mac_address_pretty();
311  response->addHeader(HEADER_PNA_ID, mac.c_str());
312  request->send(response);
313 }
314 #endif
315 
316 #ifdef USE_WEBSERVER_CSS_INCLUDE
317 void WebServer::handle_css_request(AsyncWebServerRequest *request) {
318  AsyncWebServerResponse *response =
319  request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
320  response->addHeader("Content-Encoding", "gzip");
321  request->send(response);
322 }
323 #endif
324 
325 #ifdef USE_WEBSERVER_JS_INCLUDE
326 void WebServer::handle_js_request(AsyncWebServerRequest *request) {
327  AsyncWebServerResponse *response =
328  request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
329  response->addHeader("Content-Encoding", "gzip");
330  request->send(response);
331 }
332 #endif
333 
334 #define set_json_id(root, obj, sensor, start_config) \
335  (root)["id"] = sensor; \
336  if (((start_config) == DETAIL_ALL)) { \
337  (root)["name"] = (obj)->get_name(); \
338  (root)["icon"] = (obj)->get_icon(); \
339  (root)["entity_category"] = (obj)->get_entity_category(); \
340  if ((obj)->is_disabled_by_default()) \
341  (root)["is_disabled_by_default"] = (obj)->is_disabled_by_default(); \
342  }
343 
344 #define set_json_value(root, obj, sensor, value, start_config) \
345  set_json_id((root), (obj), sensor, start_config); \
346  (root)["value"] = value;
347 
348 #define set_json_icon_state_value(root, obj, sensor, state, value, start_config) \
349  set_json_value(root, obj, sensor, value, start_config); \
350  (root)["state"] = state;
351 
352 #ifdef USE_SENSOR
354  if (this->events_.empty())
355  return;
357 }
358 void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
359  for (sensor::Sensor *obj : App.get_sensors()) {
360  if (obj->get_object_id() != match.id)
361  continue;
362  if (request->method() == HTTP_GET && match.method.empty()) {
363  auto detail = DETAIL_STATE;
364  auto *param = request->getParam("detail");
365  if (param && param->value() == "all") {
366  detail = DETAIL_ALL;
367  }
368  std::string data = this->sensor_json(obj, obj->state, detail);
369  request->send(200, "application/json", data.c_str());
370  return;
371  }
372  }
373  request->send(404);
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 }
381 std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
382  return json::build_json([this, obj, value, start_config](JsonObject root) {
383  std::string state;
384  if (std::isnan(value)) {
385  state = "NA";
386  } else {
387  state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
388  if (!obj->get_unit_of_measurement().empty())
389  state += " " + obj->get_unit_of_measurement();
390  }
391  set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
392  if (start_config == DETAIL_ALL) {
393  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
394  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
395  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
396  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
397  }
398  }
399  if (!obj->get_unit_of_measurement().empty())
400  root["uom"] = obj->get_unit_of_measurement();
401  }
402  });
403 }
404 #endif
405 
406 #ifdef USE_TEXT_SENSOR
408  if (this->events_.empty())
409  return;
411 }
412 void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
414  if (obj->get_object_id() != match.id)
415  continue;
416  if (request->method() == HTTP_GET && match.method.empty()) {
417  auto detail = DETAIL_STATE;
418  auto *param = request->getParam("detail");
419  if (param && param->value() == "all") {
420  detail = DETAIL_ALL;
421  }
422  std::string data = this->text_sensor_json(obj, obj->state, detail);
423  request->send(200, "application/json", data.c_str());
424  return;
425  }
426  }
427  request->send(404);
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 }
437 std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
438  JsonDetail start_config) {
439  return json::build_json([this, obj, value, start_config](JsonObject root) {
440  set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
441  if (start_config == DETAIL_ALL) {
442  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
443  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
444  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
445  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
446  }
447  }
448  }
449  });
450 }
451 #endif
452 
453 #ifdef USE_SWITCH
455  if (this->events_.empty())
456  return;
458 }
459 void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
460  for (switch_::Switch *obj : App.get_switches()) {
461  if (obj->get_object_id() != match.id)
462  continue;
463 
464  if (request->method() == HTTP_GET && match.method.empty()) {
465  auto detail = DETAIL_STATE;
466  auto *param = request->getParam("detail");
467  if (param && param->value() == "all") {
468  detail = DETAIL_ALL;
469  }
470  std::string data = this->switch_json(obj, obj->state, detail);
471  request->send(200, "application/json", data.c_str());
472  } else if (match.method == "toggle") {
473  this->schedule_([obj]() { obj->toggle(); });
474  request->send(200);
475  } else if (match.method == "turn_on") {
476  this->schedule_([obj]() { obj->turn_on(); });
477  request->send(200);
478  } else if (match.method == "turn_off") {
479  this->schedule_([obj]() { obj->turn_off(); });
480  request->send(200);
481  } else {
482  request->send(404);
483  }
484  return;
485  }
486  request->send(404);
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 }
494 std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
495  return json::build_json([this, obj, value, start_config](JsonObject root) {
496  set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
497  if (start_config == DETAIL_ALL) {
498  root["assumed_state"] = obj->assumed_state();
499  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
500  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
501  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
502  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
503  }
504  }
505  }
506  });
507 }
508 #endif
509 
510 #ifdef USE_BUTTON
511 void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
512  for (button::Button *obj : App.get_buttons()) {
513  if (obj->get_object_id() != match.id)
514  continue;
515  if (request->method() == HTTP_GET && match.method.empty()) {
516  auto detail = DETAIL_STATE;
517  auto *param = request->getParam("detail");
518  if (param && param->value() == "all") {
519  detail = DETAIL_ALL;
520  }
521  std::string data = this->button_json(obj, detail);
522  request->send(200, "application/json", data.c_str());
523  } else if (match.method == "press") {
524  this->schedule_([obj]() { obj->press(); });
525  request->send(200);
526  return;
527  } else {
528  request->send(404);
529  }
530  return;
531  }
532  request->send(404);
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 }
540 std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
541  return json::build_json([this, obj, start_config](JsonObject root) {
542  set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
543  if (start_config == DETAIL_ALL) {
544  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
545  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
546  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
547  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
548  }
549  }
550  }
551  });
552 }
553 #endif
554 
555 #ifdef USE_BINARY_SENSOR
557  if (this->events_.empty())
558  return;
560 }
561 void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
563  if (obj->get_object_id() != match.id)
564  continue;
565  if (request->method() == HTTP_GET && match.method.empty()) {
566  auto detail = DETAIL_STATE;
567  auto *param = request->getParam("detail");
568  if (param && param->value() == "all") {
569  detail = DETAIL_ALL;
570  }
571  std::string data = this->binary_sensor_json(obj, obj->state, detail);
572  request->send(200, "application/json", data.c_str());
573  return;
574  }
575  }
576  request->send(404);
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),
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 }
586 std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
587  return json::build_json([this, obj, value, start_config](JsonObject root) {
588  set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
589  start_config);
590  if (start_config == DETAIL_ALL) {
591  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
592  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
593  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
594  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
595  }
596  }
597  }
598  });
599 }
600 #endif
601 
602 #ifdef USE_FAN
604  if (this->events_.empty())
605  return;
607 }
608 void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
609  for (fan::Fan *obj : App.get_fans()) {
610  if (obj->get_object_id() != match.id)
611  continue;
612 
613  if (request->method() == HTTP_GET && match.method.empty()) {
614  auto detail = DETAIL_STATE;
615  auto *param = request->getParam("detail");
616  if (param && param->value() == "all") {
617  detail = DETAIL_ALL;
618  }
619  std::string data = this->fan_json(obj, detail);
620  request->send(200, "application/json", data.c_str());
621  } else if (match.method == "toggle") {
622  this->schedule_([obj]() { obj->toggle().perform(); });
623  request->send(200);
624  } else if (match.method == "turn_on" || match.method == "turn_off") {
625  auto call = match.method == "turn_on" ? obj->turn_on() : obj->turn_off();
626 
627  if (request->hasParam("speed_level")) {
628  auto speed_level = request->getParam("speed_level")->value();
629  auto val = parse_number<int>(speed_level.c_str());
630  if (!val.has_value()) {
631  ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
632  return;
633  }
634  call.set_speed(*val);
635  }
636  if (request->hasParam("oscillation")) {
637  auto speed = request->getParam("oscillation")->value();
638  auto val = parse_on_off(speed.c_str());
639  switch (val) {
640  case PARSE_ON:
641  call.set_oscillating(true);
642  break;
643  case PARSE_OFF:
644  call.set_oscillating(false);
645  break;
646  case PARSE_TOGGLE:
647  call.set_oscillating(!obj->oscillating);
648  break;
649  case PARSE_NONE:
650  request->send(404);
651  return;
652  }
653  }
654  this->schedule_([call]() mutable { call.perform(); });
655  request->send(200);
656  } else {
657  request->send(404);
658  }
659  return;
660  }
661  request->send(404);
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 }
669 std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
670  return json::build_json([this, obj, start_config](JsonObject root) {
671  set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
672  start_config);
673  const auto traits = obj->get_traits();
674  if (traits.supports_speed()) {
675  root["speed_level"] = obj->speed;
676  root["speed_count"] = traits.supported_speed_count();
677  }
678  if (obj->get_traits().supports_oscillation())
679  root["oscillation"] = obj->oscillating;
680  if (start_config == DETAIL_ALL) {
681  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
682  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
683  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
684  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
685  }
686  }
687  }
688  });
689 }
690 #endif
691 
692 #ifdef USE_LIGHT
694  if (this->events_.empty())
695  return;
697 }
698 void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
699  for (light::LightState *obj : App.get_lights()) {
700  if (obj->get_object_id() != match.id)
701  continue;
702 
703  if (request->method() == HTTP_GET && match.method.empty()) {
704  auto detail = DETAIL_STATE;
705  auto *param = request->getParam("detail");
706  if (param && param->value() == "all") {
707  detail = DETAIL_ALL;
708  }
709  std::string data = this->light_json(obj, detail);
710  request->send(200, "application/json", data.c_str());
711  } else if (match.method == "toggle") {
712  this->schedule_([obj]() { obj->toggle().perform(); });
713  request->send(200);
714  } else if (match.method == "turn_on") {
715  auto call = obj->turn_on();
716  if (request->hasParam("brightness")) {
717  auto brightness = parse_number<float>(request->getParam("brightness")->value().c_str());
718  if (brightness.has_value()) {
719  call.set_brightness(*brightness / 255.0f);
720  }
721  }
722  if (request->hasParam("r")) {
723  auto r = parse_number<float>(request->getParam("r")->value().c_str());
724  if (r.has_value()) {
725  call.set_red(*r / 255.0f);
726  }
727  }
728  if (request->hasParam("g")) {
729  auto g = parse_number<float>(request->getParam("g")->value().c_str());
730  if (g.has_value()) {
731  call.set_green(*g / 255.0f);
732  }
733  }
734  if (request->hasParam("b")) {
735  auto b = parse_number<float>(request->getParam("b")->value().c_str());
736  if (b.has_value()) {
737  call.set_blue(*b / 255.0f);
738  }
739  }
740  if (request->hasParam("white_value")) {
741  auto white_value = parse_number<float>(request->getParam("white_value")->value().c_str());
742  if (white_value.has_value()) {
743  call.set_white(*white_value / 255.0f);
744  }
745  }
746  if (request->hasParam("color_temp")) {
747  auto color_temp = parse_number<float>(request->getParam("color_temp")->value().c_str());
748  if (color_temp.has_value()) {
749  call.set_color_temperature(*color_temp);
750  }
751  }
752  if (request->hasParam("flash")) {
753  auto flash = parse_number<uint32_t>(request->getParam("flash")->value().c_str());
754  if (flash.has_value()) {
755  call.set_flash_length(*flash * 1000);
756  }
757  }
758  if (request->hasParam("transition")) {
759  auto transition = parse_number<uint32_t>(request->getParam("transition")->value().c_str());
760  if (transition.has_value()) {
761  call.set_transition_length(*transition * 1000);
762  }
763  }
764  if (request->hasParam("effect")) {
765  const char *effect = request->getParam("effect")->value().c_str();
766  call.set_effect(effect);
767  }
768 
769  this->schedule_([call]() mutable { call.perform(); });
770  request->send(200);
771  } else if (match.method == "turn_off") {
772  auto call = obj->turn_off();
773  if (request->hasParam("transition")) {
774  auto transition = parse_number<uint32_t>(request->getParam("transition")->value().c_str());
775  if (transition.has_value()) {
776  call.set_transition_length(*transition * 1000);
777  }
778  }
779  this->schedule_([call]() mutable { call.perform(); });
780  request->send(200);
781  } else {
782  request->send(404);
783  }
784  return;
785  }
786  request->send(404);
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 }
794 std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
795  return json::build_json([this, obj, start_config](JsonObject root) {
796  set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
797  root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
798 
800  if (start_config == DETAIL_ALL) {
801  JsonArray opt = root.createNestedArray("effects");
802  opt.add("None");
803  for (auto const &option : obj->get_effects()) {
804  opt.add(option->get_name());
805  }
806  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
807  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
808  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
809  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
810  }
811  }
812  }
813  });
814 }
815 #endif
816 
817 #ifdef USE_COVER
819  if (this->events_.empty())
820  return;
822 }
823 void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
824  for (cover::Cover *obj : App.get_covers()) {
825  if (obj->get_object_id() != match.id)
826  continue;
827 
828  if (request->method() == HTTP_GET && match.method.empty()) {
829  auto detail = DETAIL_STATE;
830  auto *param = request->getParam("detail");
831  if (param && param->value() == "all") {
832  detail = DETAIL_ALL;
833  }
834  std::string data = this->cover_json(obj, detail);
835  request->send(200, "application/json", data.c_str());
836  return;
837  }
838 
839  auto call = obj->make_call();
840  if (match.method == "open") {
841  call.set_command_open();
842  } else if (match.method == "close") {
843  call.set_command_close();
844  } else if (match.method == "stop") {
845  call.set_command_stop();
846  } else if (match.method == "toggle") {
847  call.set_command_toggle();
848  } else if (match.method != "set") {
849  request->send(404);
850  return;
851  }
852 
853  auto traits = obj->get_traits();
854  if ((request->hasParam("position") && !traits.get_supports_position()) ||
855  (request->hasParam("tilt") && !traits.get_supports_tilt())) {
856  request->send(409);
857  return;
858  }
859 
860  if (request->hasParam("position")) {
861  auto position = parse_number<float>(request->getParam("position")->value().c_str());
862  if (position.has_value()) {
863  call.set_position(*position);
864  }
865  }
866  if (request->hasParam("tilt")) {
867  auto tilt = parse_number<float>(request->getParam("tilt")->value().c_str());
868  if (tilt.has_value()) {
869  call.set_tilt(*tilt);
870  }
871  }
872 
873  this->schedule_([call]() mutable { call.perform(); });
874  request->send(200);
875  return;
876  }
877  request->send(404);
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 }
885 std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
886  return json::build_json([this, obj, start_config](JsonObject root) {
887  set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
888  obj->position, start_config);
889  root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
890 
891  if (obj->get_traits().get_supports_position())
892  root["position"] = obj->position;
893  if (obj->get_traits().get_supports_tilt())
894  root["tilt"] = obj->tilt;
895  if (start_config == DETAIL_ALL) {
896  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
897  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
898  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
899  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
900  }
901  }
902  }
903  });
904 }
905 #endif
906 
907 #ifdef USE_NUMBER
909  if (this->events_.empty())
910  return;
912 }
913 void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
914  for (auto *obj : App.get_numbers()) {
915  if (obj->get_object_id() != match.id)
916  continue;
917 
918  if (request->method() == HTTP_GET && match.method.empty()) {
919  auto detail = DETAIL_STATE;
920  auto *param = request->getParam("detail");
921  if (param && param->value() == "all") {
922  detail = DETAIL_ALL;
923  }
924  std::string data = this->number_json(obj, obj->state, detail);
925  request->send(200, "application/json", data.c_str());
926  return;
927  }
928  if (match.method != "set") {
929  request->send(404);
930  return;
931  }
932 
933  auto call = obj->make_call();
934  if (request->hasParam("value")) {
935  auto value = parse_number<float>(request->getParam("value")->value().c_str());
936  if (value.has_value())
937  call.set_value(*value);
938  }
939 
940  this->schedule_([call]() mutable { call.perform(); });
941  request->send(200);
942  return;
943  }
944  request->send(404);
945 }
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 }
953 std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
954  return json::build_json([this, obj, value, start_config](JsonObject root) {
955  set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
956  if (start_config == DETAIL_ALL) {
957  root["min_value"] =
959  root["max_value"] =
961  root["step"] =
963  root["mode"] = (int) obj->traits.get_mode();
964  if (!obj->traits.get_unit_of_measurement().empty())
965  root["uom"] = obj->traits.get_unit_of_measurement();
966  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
967  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
968  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
969  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
970  }
971  }
972  }
973  if (std::isnan(value)) {
974  root["value"] = "\"NaN\"";
975  root["state"] = "NA";
976  } else {
977  root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
979  if (!obj->traits.get_unit_of_measurement().empty())
980  state += " " + obj->traits.get_unit_of_measurement();
981  root["state"] = state;
982  }
983  });
984 }
985 #endif
986 
987 #ifdef USE_DATETIME_DATE
989  if (this->events_.empty())
990  return;
992 }
993 void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
994  for (auto *obj : App.get_dates()) {
995  if (obj->get_object_id() != match.id)
996  continue;
997  if (request->method() == HTTP_GET && match.method.empty()) {
998  auto detail = DETAIL_STATE;
999  auto *param = request->getParam("detail");
1000  if (param && param->value() == "all") {
1001  detail = DETAIL_ALL;
1002  }
1003  std::string data = this->date_json(obj, detail);
1004  request->send(200, "application/json", data.c_str());
1005  return;
1006  }
1007  if (match.method != "set") {
1008  request->send(404);
1009  return;
1010  }
1011 
1012  auto call = obj->make_call();
1013 
1014  if (!request->hasParam("value")) {
1015  request->send(409);
1016  return;
1017  }
1018 
1019  if (request->hasParam("value")) {
1020  std::string value = request->getParam("value")->value().c_str(); // NOLINT
1021  call.set_date(value);
1022  }
1023 
1024  this->schedule_([call]() mutable { call.perform(); });
1025  request->send(200);
1026  return;
1027  }
1028  request->send(404);
1029 }
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 }
1037 std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
1038  return json::build_json([this, obj, start_config](JsonObject root) {
1039  set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
1040  std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
1041  root["value"] = value;
1042  root["state"] = value;
1043  if (start_config == DETAIL_ALL) {
1044  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1045  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1046  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1047  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1048  }
1049  }
1050  }
1051  });
1052 }
1053 #endif // USE_DATETIME_DATE
1054 
1055 #ifdef USE_DATETIME_TIME
1057  if (this->events_.empty())
1058  return;
1060 }
1061 void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1062  for (auto *obj : App.get_times()) {
1063  if (obj->get_object_id() != match.id)
1064  continue;
1065  if (request->method() == HTTP_GET && match.method.empty()) {
1066  auto detail = DETAIL_STATE;
1067  auto *param = request->getParam("detail");
1068  if (param && param->value() == "all") {
1069  detail = DETAIL_ALL;
1070  }
1071  std::string data = this->time_json(obj, detail);
1072  request->send(200, "application/json", data.c_str());
1073  return;
1074  }
1075  if (match.method != "set") {
1076  request->send(404);
1077  return;
1078  }
1079 
1080  auto call = obj->make_call();
1081 
1082  if (!request->hasParam("value")) {
1083  request->send(409);
1084  return;
1085  }
1086 
1087  if (request->hasParam("value")) {
1088  std::string value = request->getParam("value")->value().c_str(); // NOLINT
1089  call.set_time(value);
1090  }
1091 
1092  this->schedule_([call]() mutable { call.perform(); });
1093  request->send(200);
1094  return;
1095  }
1096  request->send(404);
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 }
1104 std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
1105  return json::build_json([this, obj, start_config](JsonObject root) {
1106  set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
1107  std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
1108  root["value"] = value;
1109  root["state"] = value;
1110  if (start_config == DETAIL_ALL) {
1111  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1112  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1113  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1114  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1115  }
1116  }
1117  }
1118  });
1119 }
1120 #endif // USE_DATETIME_TIME
1121 
1122 #ifdef USE_DATETIME_DATETIME
1124  if (this->events_.empty())
1125  return;
1127 }
1128 void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1129  for (auto *obj : App.get_datetimes()) {
1130  if (obj->get_object_id() != match.id)
1131  continue;
1132  if (request->method() == HTTP_GET && match.method.empty()) {
1133  auto detail = DETAIL_STATE;
1134  auto *param = request->getParam("detail");
1135  if (param && param->value() == "all") {
1136  detail = DETAIL_ALL;
1137  }
1138  std::string data = this->datetime_json(obj, detail);
1139  request->send(200, "application/json", data.c_str());
1140  return;
1141  }
1142  if (match.method != "set") {
1143  request->send(404);
1144  return;
1145  }
1146 
1147  auto call = obj->make_call();
1148 
1149  if (!request->hasParam("value")) {
1150  request->send(409);
1151  return;
1152  }
1153 
1154  if (request->hasParam("value")) {
1155  std::string value = request->getParam("value")->value().c_str(); // NOLINT
1156  call.set_datetime(value);
1157  }
1158 
1159  this->schedule_([call]() mutable { call.perform(); });
1160  request->send(200);
1161  return;
1162  }
1163  request->send(404);
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 }
1172  return json::build_json([this, obj, start_config](JsonObject root) {
1173  set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
1174  std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour,
1175  obj->minute, obj->second);
1176  root["value"] = value;
1177  root["state"] = value;
1178  if (start_config == DETAIL_ALL) {
1179  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1180  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1181  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1182  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1183  }
1184  }
1185  }
1186  });
1187 }
1188 #endif // USE_DATETIME_DATETIME
1189 
1190 #ifdef USE_TEXT
1191 void WebServer::on_text_update(text::Text *obj, const std::string &state) {
1192  if (this->events_.empty())
1193  return;
1195 }
1196 void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1197  for (auto *obj : App.get_texts()) {
1198  if (obj->get_object_id() != match.id)
1199  continue;
1200 
1201  if (request->method() == HTTP_GET && match.method.empty()) {
1202  auto detail = DETAIL_STATE;
1203  auto *param = request->getParam("detail");
1204  if (param && param->value() == "all") {
1205  detail = DETAIL_ALL;
1206  }
1207  std::string data = this->text_json(obj, obj->state, detail);
1208  request->send(200, "application/json", data.c_str());
1209  return;
1210  }
1211  if (match.method != "set") {
1212  request->send(404);
1213  return;
1214  }
1215 
1216  auto call = obj->make_call();
1217  if (request->hasParam("value")) {
1218  String value = request->getParam("value")->value();
1219  call.set_value(value.c_str()); // NOLINT
1220  }
1221 
1222  this->defer([call]() mutable { call.perform(); });
1223  request->send(200);
1224  return;
1225  }
1226  request->send(404);
1227 }
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 }
1235 std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
1236  return json::build_json([this, obj, value, start_config](JsonObject root) {
1237  set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
1238  root["min_length"] = obj->traits.get_min_length();
1239  root["max_length"] = obj->traits.get_max_length();
1240  root["pattern"] = obj->traits.get_pattern();
1242  root["state"] = "********";
1243  } else {
1244  root["state"] = value;
1245  }
1246  root["value"] = value;
1247  if (start_config == DETAIL_ALL) {
1248  root["mode"] = (int) obj->traits.get_mode();
1249  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1250  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1251  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1252  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1253  }
1254  }
1255  }
1256  });
1257 }
1258 #endif
1259 
1260 #ifdef USE_SELECT
1261 void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
1262  if (this->events_.empty())
1263  return;
1265 }
1266 void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1267  for (auto *obj : App.get_selects()) {
1268  if (obj->get_object_id() != match.id)
1269  continue;
1270 
1271  if (request->method() == HTTP_GET && match.method.empty()) {
1272  auto detail = DETAIL_STATE;
1273  auto *param = request->getParam("detail");
1274  if (param && param->value() == "all") {
1275  detail = DETAIL_ALL;
1276  }
1277  std::string data = this->select_json(obj, obj->state, detail);
1278  request->send(200, "application/json", data.c_str());
1279  return;
1280  }
1281 
1282  if (match.method != "set") {
1283  request->send(404);
1284  return;
1285  }
1286 
1287  auto call = obj->make_call();
1288 
1289  if (request->hasParam("option")) {
1290  auto option = request->getParam("option")->value();
1291  call.set_option(option.c_str()); // NOLINT
1292  }
1293 
1294  this->schedule_([call]() mutable { call.perform(); });
1295  request->send(200);
1296  return;
1297  }
1298  request->send(404);
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 }
1306 std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
1307  return json::build_json([this, obj, value, start_config](JsonObject root) {
1308  set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
1309  if (start_config == DETAIL_ALL) {
1310  JsonArray opt = root.createNestedArray("option");
1311  for (auto &option : obj->traits.get_options()) {
1312  opt.add(option);
1313  }
1314  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1315  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1316  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1317  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1318  }
1319  }
1320  }
1321  });
1322 }
1323 #endif
1324 
1325 // Longest: HORIZONTAL
1326 #define PSTR_LOCAL(mode_s) strncpy_P(buf, (PGM_P) ((mode_s)), 15)
1327 
1328 #ifdef USE_CLIMATE
1330  if (this->events_.empty())
1331  return;
1333 }
1334 void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1335  for (auto *obj : App.get_climates()) {
1336  if (obj->get_object_id() != match.id)
1337  continue;
1338 
1339  if (request->method() == HTTP_GET && match.method.empty()) {
1340  auto detail = DETAIL_STATE;
1341  auto *param = request->getParam("detail");
1342  if (param && param->value() == "all") {
1343  detail = DETAIL_ALL;
1344  }
1345  std::string data = this->climate_json(obj, detail);
1346  request->send(200, "application/json", data.c_str());
1347  return;
1348  }
1349 
1350  if (match.method != "set") {
1351  request->send(404);
1352  return;
1353  }
1354 
1355  auto call = obj->make_call();
1356 
1357  if (request->hasParam("mode")) {
1358  auto mode = request->getParam("mode")->value();
1359  call.set_mode(mode.c_str()); // NOLINT
1360  }
1361 
1362  if (request->hasParam("fan_mode")) {
1363  auto mode = request->getParam("fan_mode")->value();
1364  call.set_fan_mode(mode.c_str()); // NOLINT
1365  }
1366 
1367  if (request->hasParam("swing_mode")) {
1368  auto mode = request->getParam("swing_mode")->value();
1369  call.set_swing_mode(mode.c_str()); // NOLINT
1370  }
1371 
1372  if (request->hasParam("target_temperature_high")) {
1373  auto target_temperature_high = parse_number<float>(request->getParam("target_temperature_high")->value().c_str());
1374  if (target_temperature_high.has_value())
1375  call.set_target_temperature_high(*target_temperature_high);
1376  }
1377 
1378  if (request->hasParam("target_temperature_low")) {
1379  auto target_temperature_low = parse_number<float>(request->getParam("target_temperature_low")->value().c_str());
1380  if (target_temperature_low.has_value())
1381  call.set_target_temperature_low(*target_temperature_low);
1382  }
1383 
1384  if (request->hasParam("target_temperature")) {
1385  auto target_temperature = parse_number<float>(request->getParam("target_temperature")->value().c_str());
1386  if (target_temperature.has_value())
1387  call.set_target_temperature(*target_temperature);
1388  }
1389 
1390  this->schedule_([call]() mutable { call.perform(); });
1391  request->send(200);
1392  return;
1393  }
1394  request->send(404);
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 }
1402 std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
1403  return json::build_json([this, obj, start_config](JsonObject root) {
1404  set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
1405  const auto traits = obj->get_traits();
1406  int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
1407  int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
1408  char buf[16];
1409 
1410  if (start_config == DETAIL_ALL) {
1411  JsonArray opt = root.createNestedArray("modes");
1412  for (climate::ClimateMode m : traits.get_supported_modes())
1413  opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
1414  if (!traits.get_supported_custom_fan_modes().empty()) {
1415  JsonArray opt = root.createNestedArray("fan_modes");
1416  for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
1417  opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
1418  }
1419 
1420  if (!traits.get_supported_custom_fan_modes().empty()) {
1421  JsonArray opt = root.createNestedArray("custom_fan_modes");
1422  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
1423  opt.add(custom_fan_mode);
1424  }
1425  if (traits.get_supports_swing_modes()) {
1426  JsonArray opt = root.createNestedArray("swing_modes");
1427  for (auto swing_mode : traits.get_supported_swing_modes())
1428  opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
1429  }
1430  if (traits.get_supports_presets() && obj->preset.has_value()) {
1431  JsonArray opt = root.createNestedArray("presets");
1432  for (climate::ClimatePreset m : traits.get_supported_presets())
1433  opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
1434  }
1435  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1436  JsonArray opt = root.createNestedArray("custom_presets");
1437  for (auto const &custom_preset : traits.get_supported_custom_presets())
1438  opt.add(custom_preset);
1439  }
1440  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1441  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1442  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1443  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1444  }
1445  }
1446  }
1447 
1448  bool has_state = false;
1449  root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
1450  root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
1451  root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
1452  root["step"] = traits.get_visual_target_temperature_step();
1453  if (traits.get_supports_action()) {
1454  root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
1455  root["state"] = root["action"];
1456  has_state = true;
1457  }
1458  if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
1459  root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
1460  }
1461  if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
1462  root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
1463  }
1464  if (traits.get_supports_presets() && obj->preset.has_value()) {
1465  root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
1466  }
1467  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1468  root["custom_preset"] = obj->custom_preset.value().c_str();
1469  }
1470  if (traits.get_supports_swing_modes()) {
1471  root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
1472  }
1473  if (traits.get_supports_current_temperature()) {
1474  if (!std::isnan(obj->current_temperature)) {
1475  root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
1476  } else {
1477  root["current_temperature"] = "NA";
1478  }
1479  }
1480  if (traits.get_supports_two_point_target_temperature()) {
1481  root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
1482  root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
1483  if (!has_state) {
1484  root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
1485  target_accuracy);
1486  }
1487  } else {
1488  root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
1489  if (!has_state)
1490  root["state"] = root["target_temperature"];
1491  }
1492  });
1493 }
1494 #endif
1495 
1496 #ifdef USE_LOCK
1498  if (this->events_.empty())
1499  return;
1501 }
1502 void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1503  for (lock::Lock *obj : App.get_locks()) {
1504  if (obj->get_object_id() != match.id)
1505  continue;
1506 
1507  if (request->method() == HTTP_GET && match.method.empty()) {
1508  auto detail = DETAIL_STATE;
1509  auto *param = request->getParam("detail");
1510  if (param && param->value() == "all") {
1511  detail = DETAIL_ALL;
1512  }
1513  std::string data = this->lock_json(obj, obj->state, detail);
1514  request->send(200, "application/json", data.c_str());
1515  } else if (match.method == "lock") {
1516  this->schedule_([obj]() { obj->lock(); });
1517  request->send(200);
1518  } else if (match.method == "unlock") {
1519  this->schedule_([obj]() { obj->unlock(); });
1520  request->send(200);
1521  } else if (match.method == "open") {
1522  this->schedule_([obj]() { obj->open(); });
1523  request->send(200);
1524  } else {
1525  request->send(404);
1526  }
1527  return;
1528  }
1529  request->send(404);
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 }
1537 std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
1538  return json::build_json([this, obj, value, start_config](JsonObject root) {
1539  set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
1540  start_config);
1541  if (start_config == DETAIL_ALL) {
1542  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1543  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1544  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1545  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1546  }
1547  }
1548  }
1549  });
1550 }
1551 #endif
1552 
1553 #ifdef USE_VALVE
1555  if (this->events_.empty())
1556  return;
1558 }
1559 void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1560  for (valve::Valve *obj : App.get_valves()) {
1561  if (obj->get_object_id() != match.id)
1562  continue;
1563 
1564  if (request->method() == HTTP_GET && match.method.empty()) {
1565  auto detail = DETAIL_STATE;
1566  auto *param = request->getParam("detail");
1567  if (param && param->value() == "all") {
1568  detail = DETAIL_ALL;
1569  }
1570  std::string data = this->valve_json(obj, detail);
1571  request->send(200, "application/json", data.c_str());
1572  return;
1573  }
1574 
1575  auto call = obj->make_call();
1576  if (match.method == "open") {
1577  call.set_command_open();
1578  } else if (match.method == "close") {
1579  call.set_command_close();
1580  } else if (match.method == "stop") {
1581  call.set_command_stop();
1582  } else if (match.method == "toggle") {
1583  call.set_command_toggle();
1584  } else if (match.method != "set") {
1585  request->send(404);
1586  return;
1587  }
1588 
1589  auto traits = obj->get_traits();
1590  if (request->hasParam("position") && !traits.get_supports_position()) {
1591  request->send(409);
1592  return;
1593  }
1594 
1595  if (request->hasParam("position")) {
1596  auto position = parse_number<float>(request->getParam("position")->value().c_str());
1597  if (position.has_value()) {
1598  call.set_position(*position);
1599  }
1600  }
1601 
1602  this->schedule_([call]() mutable { call.perform(); });
1603  request->send(200);
1604  return;
1605  }
1606  request->send(404);
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 }
1614 std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
1615  return json::build_json([this, obj, start_config](JsonObject root) {
1616  set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
1617  obj->position, start_config);
1618  root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
1619 
1620  if (obj->get_traits().get_supports_position())
1621  root["position"] = obj->position;
1622  if (start_config == DETAIL_ALL) {
1623  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1624  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1625  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1626  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1627  }
1628  }
1629  }
1630  });
1631 }
1632 #endif
1633 
1634 #ifdef USE_ALARM_CONTROL_PANEL
1636  if (this->events_.empty())
1637  return;
1639 }
1640 void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1642  if (obj->get_object_id() != match.id)
1643  continue;
1644 
1645  if (request->method() == HTTP_GET && match.method.empty()) {
1646  auto detail = DETAIL_STATE;
1647  auto *param = request->getParam("detail");
1648  if (param && param->value() == "all") {
1649  detail = DETAIL_ALL;
1650  }
1651  std::string data = this->alarm_control_panel_json(obj, obj->get_state(), detail);
1652  request->send(200, "application/json", data.c_str());
1653  return;
1654  }
1655 
1656  auto call = obj->make_call();
1657  if (request->hasParam("code")) {
1658  call.set_code(request->getParam("code")->value().c_str()); // NOLINT
1659  }
1660 
1661  if (match.method == "disarm") {
1662  call.disarm();
1663  } else if (match.method == "arm_away") {
1664  call.arm_away();
1665  } else if (match.method == "arm_home") {
1666  call.arm_home();
1667  } else if (match.method == "arm_night") {
1668  call.arm_night();
1669  } else if (match.method == "arm_vacation") {
1670  call.arm_vacation();
1671  } else {
1672  request->send(404);
1673  return;
1674  }
1675 
1676  this->schedule_([call]() mutable { call.perform(); });
1677  request->send(200);
1678  return;
1679  }
1680  request->send(404);
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 }
1694  JsonDetail start_config) {
1695  return json::build_json([this, obj, value, start_config](JsonObject root) {
1696  char buf[16];
1697  set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
1698  PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
1699  if (start_config == DETAIL_ALL) {
1700  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1701  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1702  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1703  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1704  }
1705  }
1706  }
1707  });
1708 }
1709 #endif
1710 
1711 #ifdef USE_EVENT
1712 void WebServer::on_event(event::Event *obj, const std::string &event_type) {
1714 }
1715 
1716 void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1717  for (event::Event *obj : App.get_events()) {
1718  if (obj->get_object_id() != match.id)
1719  continue;
1720 
1721  if (request->method() == HTTP_GET && match.method.empty()) {
1722  auto detail = DETAIL_STATE;
1723  auto *param = request->getParam("detail");
1724  if (param && param->value() == "all") {
1725  detail = DETAIL_ALL;
1726  }
1727  std::string data = this->event_json(obj, "", detail);
1728  request->send(200, "application/json", data.c_str());
1729  return;
1730  }
1731  }
1732  request->send(404);
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 }
1742 std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
1743  return json::build_json([this, obj, event_type, start_config](JsonObject root) {
1744  set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
1745  if (!event_type.empty()) {
1746  root["event_type"] = event_type;
1747  }
1748  if (start_config == DETAIL_ALL) {
1749  JsonArray event_types = root.createNestedArray("event_types");
1750  for (auto const &event_type : obj->get_event_types()) {
1751  event_types.add(event_type);
1752  }
1753  root["device_class"] = obj->get_device_class();
1754  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1755  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1756  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1757  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1758  }
1759  }
1760  }
1761  });
1762 }
1763 #endif
1764 
1765 #ifdef USE_UPDATE
1767  if (this->events_.empty())
1768  return;
1770 }
1771 void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1772  for (update::UpdateEntity *obj : App.get_updates()) {
1773  if (obj->get_object_id() != match.id)
1774  continue;
1775 
1776  if (request->method() == HTTP_GET && match.method.empty()) {
1777  auto detail = DETAIL_STATE;
1778  auto *param = request->getParam("detail");
1779  if (param && param->value() == "all") {
1780  detail = DETAIL_ALL;
1781  }
1782  std::string data = this->update_json(obj, detail);
1783  request->send(200, "application/json", data.c_str());
1784  return;
1785  }
1786 
1787  if (match.method != "install") {
1788  request->send(404);
1789  return;
1790  }
1791 
1792  this->schedule_([obj]() mutable { obj->perform(); });
1793  request->send(200);
1794  return;
1795  }
1796  request->send(404);
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 }
1804 std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
1805  return json::build_json([this, obj, start_config](JsonObject root) {
1806  set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
1807  root["value"] = obj->update_info.latest_version;
1808  switch (obj->state) {
1810  root["state"] = "NO UPDATE";
1811  break;
1813  root["state"] = "UPDATE AVAILABLE";
1814  break;
1816  root["state"] = "INSTALLING";
1817  break;
1818  default:
1819  root["state"] = "UNKNOWN";
1820  break;
1821  }
1822  if (start_config == DETAIL_ALL) {
1823  root["current_version"] = obj->update_info.current_version;
1824  root["title"] = obj->update_info.title;
1825  root["summary"] = obj->update_info.summary;
1826  root["release_url"] = obj->update_info.release_url;
1827  if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1828  root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1829  if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1830  root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1831  }
1832  }
1833  }
1834  });
1835 }
1836 #endif
1837 
1838 bool WebServer::canHandle(AsyncWebServerRequest *request) {
1839  if (request->url() == "/")
1840  return true;
1841 
1842 #ifdef USE_ARDUINO
1843  if (request->url() == "/events") {
1844  return true;
1845  }
1846 #endif
1847 
1848 #ifdef USE_WEBSERVER_CSS_INCLUDE
1849  if (request->url() == "/0.css")
1850  return true;
1851 #endif
1852 
1853 #ifdef USE_WEBSERVER_JS_INCLUDE
1854  if (request->url() == "/0.js")
1855  return true;
1856 #endif
1857 
1858 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
1859  if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
1860 #ifdef USE_ARDUINO
1861  // Header needs to be added to interesting header list for it to not be
1862  // nuked by the time we handle the request later.
1863  // Only required in Arduino framework.
1864  request->addInterestingHeader(HEADER_CORS_REQ_PNA);
1865 #endif
1866  return true;
1867  }
1868 #endif
1869 
1870  UrlMatch match = match_url(request->url().c_str(), true); // NOLINT
1871  if (!match.valid)
1872  return false;
1873 #ifdef USE_SENSOR
1874  if (request->method() == HTTP_GET && match.domain == "sensor")
1875  return true;
1876 #endif
1877 
1878 #ifdef USE_SWITCH
1879  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "switch")
1880  return true;
1881 #endif
1882 
1883 #ifdef USE_BUTTON
1884  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "button")
1885  return true;
1886 #endif
1887 
1888 #ifdef USE_BINARY_SENSOR
1889  if (request->method() == HTTP_GET && match.domain == "binary_sensor")
1890  return true;
1891 #endif
1892 
1893 #ifdef USE_FAN
1894  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "fan")
1895  return true;
1896 #endif
1897 
1898 #ifdef USE_LIGHT
1899  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "light")
1900  return true;
1901 #endif
1902 
1903 #ifdef USE_TEXT_SENSOR
1904  if (request->method() == HTTP_GET && match.domain == "text_sensor")
1905  return true;
1906 #endif
1907 
1908 #ifdef USE_COVER
1909  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "cover")
1910  return true;
1911 #endif
1912 
1913 #ifdef USE_NUMBER
1914  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "number")
1915  return true;
1916 #endif
1917 
1918 #ifdef USE_DATETIME_DATE
1919  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "date")
1920  return true;
1921 #endif
1922 
1923 #ifdef USE_DATETIME_TIME
1924  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "time")
1925  return true;
1926 #endif
1927 
1928 #ifdef USE_DATETIME_DATETIME
1929  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "datetime")
1930  return true;
1931 #endif
1932 
1933 #ifdef USE_TEXT
1934  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text")
1935  return true;
1936 #endif
1937 
1938 #ifdef USE_SELECT
1939  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "select")
1940  return true;
1941 #endif
1942 
1943 #ifdef USE_CLIMATE
1944  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "climate")
1945  return true;
1946 #endif
1947 
1948 #ifdef USE_LOCK
1949  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "lock")
1950  return true;
1951 #endif
1952 
1953 #ifdef USE_VALVE
1954  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "valve")
1955  return true;
1956 #endif
1957 
1958 #ifdef USE_ALARM_CONTROL_PANEL
1959  if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.domain == "alarm_control_panel")
1960  return true;
1961 #endif
1962 
1963 #ifdef USE_EVENT
1964  if (request->method() == HTTP_GET && match.domain == "event")
1965  return true;
1966 #endif
1967 
1968 #ifdef USE_UPDATE
1969  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "update")
1970  return true;
1971 #endif
1972 
1973  return false;
1974 }
1975 void WebServer::handleRequest(AsyncWebServerRequest *request) {
1976  if (request->url() == "/") {
1977  this->handle_index_request(request);
1978  return;
1979  }
1980 
1981 #ifdef USE_ARDUINO
1982  if (request->url() == "/events") {
1983  this->events_.add_new_client(this, request);
1984  return;
1985  }
1986 #endif
1987 
1988 #ifdef USE_WEBSERVER_CSS_INCLUDE
1989  if (request->url() == "/0.css") {
1990  this->handle_css_request(request);
1991  return;
1992  }
1993 #endif
1994 
1995 #ifdef USE_WEBSERVER_JS_INCLUDE
1996  if (request->url() == "/0.js") {
1997  this->handle_js_request(request);
1998  return;
1999  }
2000 #endif
2001 
2002 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
2003  if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
2004  this->handle_pna_cors_request(request);
2005  return;
2006  }
2007 #endif
2008 
2009  UrlMatch match = match_url(request->url().c_str()); // NOLINT
2010 #ifdef USE_SENSOR
2011  if (match.domain == "sensor") {
2012  this->handle_sensor_request(request, match);
2013  return;
2014  }
2015 #endif
2016 
2017 #ifdef USE_SWITCH
2018  if (match.domain == "switch") {
2019  this->handle_switch_request(request, match);
2020  return;
2021  }
2022 #endif
2023 
2024 #ifdef USE_BUTTON
2025  if (match.domain == "button") {
2026  this->handle_button_request(request, match);
2027  return;
2028  }
2029 #endif
2030 
2031 #ifdef USE_BINARY_SENSOR
2032  if (match.domain == "binary_sensor") {
2033  this->handle_binary_sensor_request(request, match);
2034  return;
2035  }
2036 #endif
2037 
2038 #ifdef USE_FAN
2039  if (match.domain == "fan") {
2040  this->handle_fan_request(request, match);
2041  return;
2042  }
2043 #endif
2044 
2045 #ifdef USE_LIGHT
2046  if (match.domain == "light") {
2047  this->handle_light_request(request, match);
2048  return;
2049  }
2050 #endif
2051 
2052 #ifdef USE_TEXT_SENSOR
2053  if (match.domain == "text_sensor") {
2054  this->handle_text_sensor_request(request, match);
2055  return;
2056  }
2057 #endif
2058 
2059 #ifdef USE_COVER
2060  if (match.domain == "cover") {
2061  this->handle_cover_request(request, match);
2062  return;
2063  }
2064 #endif
2065 
2066 #ifdef USE_NUMBER
2067  if (match.domain == "number") {
2068  this->handle_number_request(request, match);
2069  return;
2070  }
2071 #endif
2072 
2073 #ifdef USE_DATETIME_DATE
2074  if (match.domain == "date") {
2075  this->handle_date_request(request, match);
2076  return;
2077  }
2078 #endif
2079 
2080 #ifdef USE_DATETIME_TIME
2081  if (match.domain == "time") {
2082  this->handle_time_request(request, match);
2083  return;
2084  }
2085 #endif
2086 
2087 #ifdef USE_DATETIME_DATETIME
2088  if (match.domain == "datetime") {
2089  this->handle_datetime_request(request, match);
2090  return;
2091  }
2092 #endif
2093 
2094 #ifdef USE_TEXT
2095  if (match.domain == "text") {
2096  this->handle_text_request(request, match);
2097  return;
2098  }
2099 #endif
2100 
2101 #ifdef USE_SELECT
2102  if (match.domain == "select") {
2103  this->handle_select_request(request, match);
2104  return;
2105  }
2106 #endif
2107 
2108 #ifdef USE_CLIMATE
2109  if (match.domain == "climate") {
2110  this->handle_climate_request(request, match);
2111  return;
2112  }
2113 #endif
2114 
2115 #ifdef USE_LOCK
2116  if (match.domain == "lock") {
2117  this->handle_lock_request(request, match);
2118 
2119  return;
2120  }
2121 #endif
2122 
2123 #ifdef USE_VALVE
2124  if (match.domain == "valve") {
2125  this->handle_valve_request(request, match);
2126  return;
2127  }
2128 #endif
2129 
2130 #ifdef USE_ALARM_CONTROL_PANEL
2131  if (match.domain == "alarm_control_panel") {
2132  this->handle_alarm_control_panel_request(request, match);
2133 
2134  return;
2135  }
2136 #endif
2137 
2138 #ifdef USE_UPDATE
2139  if (match.domain == "update") {
2140  this->handle_update_request(request, match);
2141  return;
2142  }
2143 #endif
2144 }
2145 
2146 bool WebServer::isRequestHandlerTrivial() { return false; }
2147 
2148 void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) {
2149  this->sorting_entitys_[entity] = SortingComponents{weight, group};
2150 }
2151 
2152 void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_name, float weight) {
2153  this->sorting_groups_[group_id] = SortingGroup{group_name, weight};
2154 }
2155 
2156 void WebServer::schedule_(std::function<void()> &&f) {
2157 #ifdef USE_ESP32
2158  xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);
2159  to_schedule_.push_back(std::move(f));
2160  xSemaphoreGive(this->to_schedule_lock_);
2161 #else
2162  this->defer(std::move(f));
2163 #endif
2164 }
2165 
2166 } // namespace web_server
2167 } // namespace esphome
2168 #endif
Base class for all switches.
Definition: switch.h:39
bool state
Definition: fan.h:34
value_type const & value() const
Definition: optional.h:89
bool state
The current on/off state of the fan.
Definition: fan.h:110
const size_t ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition: climate.h:202
float target_temperature_low
Definition: climate.h:140
const std::vector< datetime::DateTimeEntity * > & get_datetimes()
Definition: application.h:367
static std::string date_all_json_generator(WebServer *web_server, void *source)
void handle_pna_cors_request(AsyncWebServerRequest *request)
Definition: web_server.cpp:306
void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a number request under &#39;/number/<id>&#39;.
Definition: web_server.cpp:913
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition: light_state.h:63
void add_new_client(WebServer *ws, AsyncWebServerRequest *request)
Definition: web_server.cpp:166
bool oscillating
The current oscillation state of the fan.
Definition: fan.h:112
void set_interval(const std::string &name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a unique name.
Definition: component.cpp:52
std::string number_json(number::Number *obj, float value, JsonDetail start_config)
Dump the number state with its value as a JSON string.
Definition: web_server.cpp:953
std::string sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config)
Dump the sensor state with its value as a JSON string.
Definition: web_server.cpp:381
void add_on_log_callback(std::function< void(int, const char *, const char *)> &&callback)
Register a callback that will be called for every log message sent.
Definition: logger.cpp:170
void on_sensor_update(sensor::Sensor *obj, float state) override
Definition: web_server.cpp:353
bool is_on() const
Get the binary true/false state of these light color values.
Base class for all cover devices.
Definition: cover.h:111
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals)
Create a string from a value and an accuracy in decimals.
Definition: helpers.cpp:436
WebServer(web_server_base::WebServerBase *base)
Definition: web_server.cpp:214
void handleRequest(AsyncWebServerRequest *request) override
Override the web handler&#39;s handleRequest method.
TextMode get_mode() const
Definition: text_traits.h:29
ClimatePreset
Enum for all preset modes.
Definition: climate_mode.h:82
void handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a time request under &#39;/time/<id>&#39;.
static std::string binary_sensor_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:582
const std::vector< climate::Climate * > & get_climates()
Definition: application.h:327
static std::string number_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:947
void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator)
Definition: web_server.cpp:77
float target_temperature
The target temperature of the climate device.
Definition: climate.h:186
SemaphoreHandle_t to_schedule_lock_
Definition: web_server.h:508
This class allows users to create a web server with their ESP nodes.
Definition: web_server.h:149
std::vector< DeferredEvent > deferred_queue_
Definition: web_server.h:107
std::string get_use_address()
Get the active network hostname.
Definition: util.cpp:52
void handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a binary sensor request under &#39;/binary_sensor/<id>&#39;.
Definition: web_server.cpp:561
std::string select_json(select::Select *obj, const std::string &value, JsonDetail start_config)
Dump the select state with its value as a JSON string.
std::string get_device_class()
Get the device class, using the manual override if set.
Definition: entity_base.cpp:78
const std::vector< update::UpdateEntity * > & get_updates()
Definition: application.h:452
const std::vector< alarm_control_panel::AlarmControlPanel * > & get_alarm_control_panels()
Definition: application.h:428
bool is_fully_closed() const
Helper method to check if the valve is fully closed. Equivalent to comparing .position against 0...
Definition: valve.cpp:166
static std::string select_state_json_generator(WebServer *web_server, void *source)
const char * lock_state_to_string(LockState state)
Definition: lock.cpp:9
void handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a select request under &#39;/select/<id>&#39;.
TextTraits traits
Definition: text.h:27
const std::vector< valve::Valve * > & get_valves()
Definition: application.h:407
static std::string binary_sensor_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:578
void handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a text input request under &#39;/text/<id>&#39;.
CoverOperation current_operation
The current operation of the cover (idle, opening, closing).
Definition: cover.h:116
std::map< EntityBase *, SortingComponents > sorting_entitys_
Definition: web_server.h:480
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator)
Definition: web_server.cpp:152
float position
The position of the valve from 0.0 (fully closed) to 1.0 (fully open).
Definition: valve.h:116
void handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a event request under &#39;/event<id>&#39;.
static std::string alarm_control_panel_state_json_generator(WebServer *web_server, void *source)
Base class for all buttons.
Definition: button.h:29
DeferredEvent(void *source, message_generator_t *message_generator)
Definition: web_server.h:82
void handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a update request under &#39;/update/<id>&#39;.
const LogString * climate_mode_to_string(ClimateMode mode)
Convert the given ClimateMode to a human-readable string.
Definition: climate_mode.cpp:6
virtual FanTraits get_traits()=0
std::set< std::string > get_event_types() const
Definition: event.h:30
void handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a valve request under &#39;/valve/<id>/<open/close/stop/set>&#39;.
static std::string select_all_json_generator(WebServer *web_server, void *source)
const UpdateState & state
Definition: update_entity.h:40
DeferredUpdateEventSource(WebServer *ws, const String &url)
Definition: web_server.h:116
void try_send_nodefer(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
Definition: web_server.cpp:159
void defer(const std::string &name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition: component.cpp:130
const std::vector< event::Event * > & get_events()
Definition: application.h:441
bool get_supports_position() const
Definition: cover_traits.h:12
ClimateMode mode
The active mode of the climate device.
Definition: climate.h:173
void on_lock_update(lock::Lock *obj) override
static std::string update_all_json_generator(WebServer *web_server, void *source)
static std::string text_sensor_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:433
int speed
Definition: fan.h:35
static std::string text_all_json_generator(WebServer *web_server, void *source)
virtual bool assumed_state()
Return whether this switch uses an assumed state - i.e.
Definition: switch.cpp:58
static std::string alarm_control_panel_all_json_generator(WebServer *web_server, void *source)
float tilt
Definition: cover.h:15
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
Definition: application.h:205
void setup() override
Setup the internal web server and register handlers.
Definition: web_server.cpp:237
SelectTraits traits
Definition: select.h:34
float target_temperature_high
The maximum target temperature of the climate device, for climate devices with split target temperatu...
Definition: climate.h:191
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition: climate.h:179
mopeka_std_values val[4]
const std::vector< fan::Fan * > & get_fans()
Definition: application.h:297
static std::string datetime_all_json_generator(WebServer *web_server, void *source)
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override
Definition: web_server.cpp:556
void handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a light request under &#39;/light/<id>/</turn_on/turn_off/toggle>&#39;.
Definition: web_server.cpp:698
bool isRequestHandlerTrivial() override
This web handle is not trivial.
void handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a lock request under &#39;/lock/<id>/</lock/unlock/open>&#39;.
static std::string text_state_json_generator(WebServer *web_server, void *source)
bool has_value() const
Definition: optional.h:87
static std::string fan_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:666
float target_temperature_high
Definition: climate.h:141
void handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a button request under &#39;/button/<id>/press&#39;.
Definition: web_server.cpp:511
int get_max_length() const
Definition: text_traits.h:21
Base-class for all text inputs.
Definition: text.h:24
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator)
Definition: web_server.cpp:109
bool supports_oscillation() const
Return if this fan supports oscillation.
Definition: fan_traits.h:16
void on_light_update(light::LightState *obj) override
Definition: web_server.cpp:693
virtual ValveTraits get_traits()=0
void on_client_disconnect_(DeferredUpdateEventSource *source)
Definition: web_server.cpp:206
void on_event(event::Event *obj, const std::string &event_type) override
float tilt
The current tilt value of the cover from 0.0 to 1.0.
Definition: cover.h:124
const std::vector< datetime::TimeEntity * > & get_times()
Definition: application.h:357
std::string get_object_id() const
Definition: entity_base.cpp:43
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
std::string event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config)
Dump the event details with its value as a JSON string.
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off)
Parse a string that contains either on, off or toggle.
Definition: helpers.cpp:421
ClimateSwingMode swing_mode
Definition: climate.h:581
void try_send_nodefer(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
Definition: web_server.cpp:141
const size_t ESPHOME_WEBSERVER_JS_INCLUDE_SIZE
Internal helper struct that is used to parse incoming URLs.
Definition: web_server.h:42
static std::string lock_state_json_generator(WebServer *web_server, void *source)
optional< std::string > custom_fan_mode
The active custom fan mode of the climate device.
Definition: climate.h:205
virtual CoverTraits get_traits()=0
static std::string event_state_json_generator(WebServer *web_server, void *source)
void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a sensor request under &#39;/sensor/<id>&#39;.
Definition: web_server.cpp:358
std::map< uint64_t, SortingGroup > sorting_groups_
Definition: web_server.h:481
const std::vector< lock::Lock * > & get_locks()
Definition: application.h:397
static std::string switch_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:488
std::string text_sensor_json(text_sensor::TextSensor *obj, const std::string &value, JsonDetail start_config)
Dump the text sensor state with its value as a JSON string.
Definition: web_server.cpp:437
static std::string time_all_json_generator(WebServer *web_server, void *source)
const size_t ESPHOME_WEBSERVER_INDEX_HTML_SIZE
std::string domain
The domain of the component, for example "sensor".
Definition: web_server.h:43
std::string text_json(text::Text *obj, const std::string &value, JsonDetail start_config)
Dump the text state with its value as a JSON string.
std::string update_json(update::UpdateEntity *obj, JsonDetail start_config)
Dump the update state with its value as a JSON string.
Logger * global_logger
Definition: logger.cpp:200
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override
Definition: web_server.cpp:407
const char *const TAG
Definition: spi.cpp:8
void add_handler(AsyncWebHandler *handler)
DeferredUpdateEventSourceList events_
Definition: web_server.h:488
void set_css_include(const char *css_include)
Set local path to the script that&#39;s embedded in the index page.
Definition: web_server.cpp:221
void on_client_connect_(WebServer *ws, DeferredUpdateEventSource *source)
Definition: web_server.cpp:181
void on_text_update(text::Text *obj, const std::string &state) override
static std::string switch_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:491
const std::vector< button::Button * > & get_buttons()
Definition: application.h:267
void handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a switch request under &#39;/switch/<id>/</turn_on/turn_off/toggle>&#39;.
Definition: web_server.cpp:459
const LogString * alarm_control_panel_state_to_string(AlarmControlPanelState state)
Returns a string representation of the state.
static std::string valve_all_json_generator(WebServer *web_server, void *source)
std::vector< std::string > get_options() const
static std::string cover_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:882
optional< ClimatePreset > preset
The active preset of the climate device.
Definition: climate.h:208
const UpdateInfo & update_info
Definition: update_entity.h:39
static std::string button_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:534
static std::string event_all_json_generator(WebServer *web_server, void *source)
uint8_t custom_preset
Definition: climate.h:579
UrlMatch match_url(const std::string &url, bool only_domain=false)
Definition: web_server.cpp:49
const std::vector< switch_::Switch * > & get_switches()
Definition: application.h:257
static std::string date_state_json_generator(WebServer *web_server, void *source)
Base-class for all numbers.
Definition: number.h:39
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:324
const char * cover_operation_to_str(CoverOperation op)
Definition: cover.cpp:21
int speed
The current fan speed level.
Definition: fan.h:114
void handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a alarm_control_panel request under &#39;/alarm_control_panel/<id>&#39;.
void handle_css_request(AsyncWebServerRequest *request)
Handle included css request under &#39;/0.css&#39;.
Definition: web_server.cpp:317
static std::string time_state_json_generator(WebServer *web_server, void *source)
bool valid
Whether this match is valid.
Definition: web_server.h:46
static std::string climate_all_json_generator(WebServer *web_server, void *source)
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
bool is_fully_closed() const
Helper method to check if the cover is fully closed. Equivalent to comparing .position against 0...
Definition: cover.cpp:209
void on_select_update(select::Select *obj, const std::string &state, size_t index) override
ClimateTraits get_traits()
Get the traits of this climate device with all overrides applied.
Definition: climate.cpp:440
std::string get_unit_of_measurement()
Get the unit of measurement, using the manual override if set.
Definition: entity_base.cpp:87
std::string time_json(datetime::TimeEntity *obj, JsonDetail start_config)
Dump the time state with its value as a JSON string.
const std::vector< text_sensor::TextSensor * > & get_text_sensors()
Definition: application.h:287
const LogString * climate_preset_to_string(ClimatePreset preset)
Convert the given PresetMode to a human-readable string.
int8_t get_target_temperature_accuracy_decimals() const
const std::vector< sensor::Sensor * > & get_sensors()
Definition: application.h:277
Application App
Global storage of Application pointer - only one Application can exist.
static std::string button_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:537
const std::vector< binary_sensor::BinarySensor * > & get_binary_sensors()
Definition: application.h:247
const std::vector< LightEffect * > & get_effects() const
Get all effects for this light state.
static std::string valve_state_json_generator(WebServer *web_server, void *source)
void handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a datetime request under &#39;/datetime/<id>&#39;.
static std::string update_state_json_generator(WebServer *web_server, void *source)
bool get_supports_position() const
Definition: valve_traits.h:12
int8_t step_to_accuracy_decimals(float step)
Derive accuracy in decimals from an increment step.
Definition: helpers.cpp:447
std::string switch_json(switch_::Switch *obj, bool value, JsonDetail start_config)
Dump the switch state with its value as a JSON string.
Definition: web_server.cpp:494
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition: json_util.cpp:12
void begin(bool include_internal=false)
static std::string number_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:950
static std::string fan_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:663
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:202
std::string light_json(light::LightState *obj, JsonDetail start_config)
Dump the light state as a JSON string.
Definition: web_server.cpp:794
void add_sorting_group(uint64_t group_id, const std::string &group_name, float weight)
void on_valve_update(valve::Valve *obj) override
static void dump_json(LightState &state, JsonObject root)
Dump the state of a light as JSON.
void add_entity_config(EntityBase *entity, float weight, uint64_t group)
const std::vector< text::Text * > & get_texts()
Definition: application.h:377
ClimateMode
Enum for all modes a climate device can be in.
Definition: climate_mode.h:10
void handle_index_request(AsyncWebServerRequest *request)
Handle an index request under &#39;/&#39;.
Definition: web_server.cpp:291
std::string valve_json(valve::Valve *obj, JsonDetail start_config)
Dump the valve state as a JSON string.
NumberTraits traits
Definition: number.h:49
void on_time_update(datetime::TimeEntity *obj) override
void on_climate_update(climate::Climate *obj) override
const std::vector< cover::Cover * > & get_covers()
Definition: application.h:307
float get_setup_priority() const override
MQTT setup priority.
Definition: web_server.cpp:288
optional< std::string > custom_preset
The active custom preset mode of the climate device.
Definition: climate.h:211
static std::string cover_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:879
const LogString * climate_fan_mode_to_string(ClimateFanMode fan_mode)
Convert the given ClimateFanMode to a human-readable string.
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition: climate.h:199
float position
The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
Definition: cover.h:122
void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a fan request under &#39;/fan/<id>/</turn_on/turn_off/toggle>&#39;.
Definition: web_server.cpp:608
std::string id
The id of the device that&#39;s being accessed, for example "living_room_fan".
Definition: web_server.h:44
const std::vector< light::LightState * > & get_lights()
Definition: application.h:317
void on_date_update(datetime::DateEntity *obj) override
Definition: web_server.cpp:988
std::string get_comment() const
Get the comment of this Application set by pre_setup().
Definition: application.h:211
std::string button_json(button::Button *obj, JsonDetail start_config)
Dump the button details with its value as a JSON string.
Definition: web_server.cpp:540
void on_cover_update(cover::Cover *obj) override
Definition: web_server.cpp:818
const char * valve_operation_to_str(ValveOperation op)
Definition: valve.cpp:21
void handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a cover request under &#39;/cover/<id>/<open/close/stop/set>&#39;.
Definition: web_server.cpp:823
void on_switch_update(switch_::Switch *obj, bool state) override
Definition: web_server.cpp:454
static std::string light_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:791
bool get_supports_tilt() const
Definition: cover_traits.h:14
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override
void setup_controller(bool include_internal=false)
Definition: controller.cpp:7
static std::string lock_all_json_generator(WebServer *web_server, void *source)
void on_datetime_update(datetime::DateTimeEntity *obj) override
std::string(WebServer *, void *) message_generator_t
Definition: web_server.h:70
std::string date_json(datetime::DateEntity *obj, JsonDetail start_config)
Dump the date state with its value as a JSON string.
static std::string datetime_state_json_generator(WebServer *web_server, void *source)
std::string get_config_json()
Return the webserver configuration as JSON.
Definition: web_server.cpp:227
std::string datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config)
Dump the datetime state with its value as a JSON string.
Base-class for all selects.
Definition: select.h:31
void on_fan_update(fan::Fan *obj) override
Definition: web_server.cpp:603
web_server_base::WebServerBase * base_
Definition: web_server.h:486
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
static std::string sensor_all_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:378
void on_number_update(number::Number *obj, float state) override
Definition: web_server.cpp:908
Base class for all valve devices.
Definition: valve.h:105
std::string fan_json(fan::Fan *obj, JsonDetail start_config)
Dump the fan state as a JSON string.
Definition: web_server.cpp:669
ValveOperation current_operation
The current operation of the valve (idle, opening, closing).
Definition: valve.h:110
Base class for all binary_sensor-type classes.
Definition: binary_sensor.h:37
LightColorValues remote_values
The remote color values reported to the frontend.
Definition: light_state.h:106
static std::string text_sensor_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:429
LockState
Enum for all states a lock can be in.
Definition: lock.h:26
static std::string light_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:788
NumberMode get_mode() const
Definition: number_traits.h:29
void handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a climate request under &#39;/climate/<id>&#39;.
int8_t get_accuracy_decimals()
Get the accuracy in decimals, using the manual override if set.
Definition: sensor.cpp:25
const std::vector< datetime::DateEntity * > & get_dates()
Definition: application.h:347
uint8_t m
Definition: bl0906.h:208
int get_min_length() const
Definition: text_traits.h:19
float position
Definition: cover.h:14
const std::vector< select::Select * > & get_selects()
Definition: application.h:387
Base-class for all sensors.
Definition: sensor.h:57
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition: helpers.cpp:733
bool canHandle(AsyncWebServerRequest *request) override
Override the web handler&#39;s canHandle method.
AsyncEventSourceResponse AsyncEventSourceClient
std::string alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj, alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config)
Dump the alarm_control_panel state with its value as a JSON string.
std::deque< std::function< void()> > to_schedule_
Definition: web_server.h:507
const std::vector< number::Number * > & get_numbers()
Definition: application.h:337
const LogString * climate_action_to_string(ClimateAction action)
Convert the given ClimateAction to a human-readable string.
void on_update(update::UpdateEntity *obj) override
void handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a text sensor request under &#39;/text_sensor/<id>&#39;.
Definition: web_server.cpp:412
uint8_t custom_fan_mode
Definition: climate.h:574
float target_temperature
Definition: climate.h:138
void schedule_(std::function< void()> &&f)
std::string lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config)
Dump the lock state with its value as a JSON string.
std::string get_pattern() const
Definition: text_traits.h:25
float target_temperature_low
The minimum target temperature of the climate device, for climate devices with split target temperatu...
Definition: climate.h:189
std::string climate_json(climate::Climate *obj, JsonDetail start_config)
Dump the climate details.
std::string method
The method that&#39;s being called, for example "turn_on".
Definition: web_server.h:45
void handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a date request under &#39;/date/<id>&#39;.
Definition: web_server.cpp:993
Base class for all locks.
Definition: lock.h:103
ClimateAction action
The active state of the climate device.
Definition: climate.h:176
ClimateDevice - This is the base class for all climate integrations.
Definition: climate.h:168
std::string binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config)
Dump the binary sensor state with its value as a JSON string.
Definition: web_server.cpp:586
std::string cover_json(cover::Cover *obj, JsonDetail start_config)
Dump the cover state as a JSON string.
Definition: web_server.cpp:885
void handle_js_request(AsyncWebServerRequest *request)
Handle included js request under &#39;/0.js&#39;.
Definition: web_server.cpp:326
static std::string sensor_state_json_generator(WebServer *web_server, void *source)
Definition: web_server.cpp:375
void set_js_include(const char *js_include)
Set local path to the script that&#39;s embedded in the index page.
Definition: web_server.cpp:224
static std::string climate_state_json_generator(WebServer *web_server, void *source)
const LogString * climate_swing_mode_to_string(ClimateSwingMode swing_mode)
Convert the given ClimateSwingMode to a human-readable string.