ESPHome  2024.4.1
web_server.cpp
Go to the documentation of this file.
1 #include "web_server.h"
2 
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 #include "server_index.h"
31 #endif
32 
33 namespace esphome {
34 namespace web_server {
35 
36 static const char *const TAG = "web_server";
37 
38 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
39 static const char *const HEADER_PNA_NAME = "Private-Network-Access-Name";
40 static const char *const HEADER_PNA_ID = "Private-Network-Access-ID";
41 static const char *const HEADER_CORS_REQ_PNA = "Access-Control-Request-Private-Network";
42 static const char *const HEADER_CORS_ALLOW_PNA = "Access-Control-Allow-Private-Network";
43 #endif
44 
45 #if USE_WEBSERVER_VERSION == 1
46 void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action,
47  const std::function<void(AsyncResponseStream &stream, EntityBase *obj)> &action_func = nullptr) {
48  stream->print("<tr class=\"");
49  stream->print(klass.c_str());
50  if (obj->is_internal())
51  stream->print(" internal");
52  stream->print("\" id=\"");
53  stream->print(klass.c_str());
54  stream->print("-");
55  stream->print(obj->get_object_id().c_str());
56  stream->print("\"><td>");
57  stream->print(obj->get_name().c_str());
58  stream->print("</td><td></td><td>");
59  stream->print(action.c_str());
60  if (action_func) {
61  action_func(*stream, obj);
62  }
63  stream->print("</td>");
64  stream->print("</tr>");
65 }
66 #endif
67 
68 UrlMatch match_url(const std::string &url, bool only_domain = false) {
69  UrlMatch match;
70  match.valid = false;
71  size_t domain_end = url.find('/', 1);
72  if (domain_end == std::string::npos)
73  return match;
74  match.domain = url.substr(1, domain_end - 1);
75  if (only_domain) {
76  match.valid = true;
77  return match;
78  }
79  if (url.length() == domain_end - 1)
80  return match;
81  size_t id_begin = domain_end + 1;
82  size_t id_end = url.find('/', id_begin);
83  match.valid = true;
84  if (id_end == std::string::npos) {
85  match.id = url.substr(id_begin, url.length() - id_begin);
86  return match;
87  }
88  match.id = url.substr(id_begin, id_end - id_begin);
89  size_t method_begin = id_end + 1;
90  match.method = url.substr(method_begin, url.length() - method_begin);
91  return match;
92 }
93 
95  : base_(base), entities_iterator_(ListEntitiesIterator(this)) {
96 #ifdef USE_ESP32
97  to_schedule_lock_ = xSemaphoreCreateMutex();
98 #endif
99 }
100 
101 #if USE_WEBSERVER_VERSION == 1
102 void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; }
103 void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; }
104 #endif
105 
106 #ifdef USE_WEBSERVER_CSS_INCLUDE
107 void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; }
108 #endif
109 #ifdef USE_WEBSERVER_JS_INCLUDE
110 void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; }
111 #endif
112 
114  return json::build_json([this](JsonObject root) {
115  root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
116  root["comment"] = App.get_comment();
117  root["ota"] = this->allow_ota_;
118  root["log"] = this->expose_log_;
119  root["lang"] = "en";
120  });
121 }
122 
124  ESP_LOGCONFIG(TAG, "Setting up web server...");
125  this->setup_controller(this->include_internal_);
126  this->base_->init();
127 
128  this->events_.onConnect([this](AsyncEventSourceClient *client) {
129  // Configure reconnect timeout and send config
130  client->send(this->get_config_json().c_str(), "ping", millis(), 30000);
131 
133  });
134 
135 #ifdef USE_LOGGER
136  if (logger::global_logger != nullptr && this->expose_log_) {
138  [this](int level, const char *tag, const char *message) { this->events_.send(message, "log", millis()); });
139  }
140 #endif
141  this->base_->add_handler(&this->events_);
142  this->base_->add_handler(this);
143 
144  if (this->allow_ota_)
145  this->base_->add_ota_handler();
146 
147  this->set_interval(10000, [this]() { this->events_.send("", "ping", millis(), 30000); });
148 }
150 #ifdef USE_ESP32
151  if (xSemaphoreTake(this->to_schedule_lock_, 0L)) {
152  std::function<void()> fn;
153  if (!to_schedule_.empty()) {
154  // scheduler execute things out of order which may lead to incorrect state
155  // this->defer(std::move(to_schedule_.front()));
156  // let's execute it directly from the loop
157  fn = std::move(to_schedule_.front());
158  to_schedule_.pop_front();
159  }
160  xSemaphoreGive(this->to_schedule_lock_);
161  if (fn) {
162  fn();
163  }
164  }
165 #endif
166  this->entities_iterator_.advance();
167 }
169  ESP_LOGCONFIG(TAG, "Web Server:");
170  ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port());
171 }
172 float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; }
173 
174 #ifdef USE_WEBSERVER_LOCAL
175 void WebServer::handle_index_request(AsyncWebServerRequest *request) {
176  AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
177  response->addHeader("Content-Encoding", "gzip");
178  request->send(response);
179 }
180 #elif USE_WEBSERVER_VERSION == 1
181 void WebServer::handle_index_request(AsyncWebServerRequest *request) {
182  AsyncResponseStream *stream = request->beginResponseStream("text/html");
183  const std::string &title = App.get_name();
184  stream->print(F("<!DOCTYPE html><html lang=\"en\"><head><meta charset=UTF-8><meta "
185  "name=viewport content=\"width=device-width, initial-scale=1,user-scalable=no\"><title>"));
186  stream->print(title.c_str());
187  stream->print(F("</title>"));
188 #ifdef USE_WEBSERVER_CSS_INCLUDE
189  stream->print(F("<link rel=\"stylesheet\" href=\"/0.css\">"));
190 #endif
191  if (strlen(this->css_url_) > 0) {
192  stream->print(F(R"(<link rel="stylesheet" href=")"));
193  stream->print(this->css_url_);
194  stream->print(F("\">"));
195  }
196  stream->print(F("</head><body>"));
197  stream->print(F("<article class=\"markdown-body\"><h1>"));
198  stream->print(title.c_str());
199  stream->print(F("</h1>"));
200  stream->print(F("<h2>States</h2><table id=\"states\"><thead><tr><th>Name<th>State<th>Actions<tbody>"));
201 
202 #ifdef USE_SENSOR
203  for (auto *obj : App.get_sensors()) {
204  if (this->include_internal_ || !obj->is_internal())
205  write_row(stream, obj, "sensor", "");
206  }
207 #endif
208 
209 #ifdef USE_SWITCH
210  for (auto *obj : App.get_switches()) {
211  if (this->include_internal_ || !obj->is_internal())
212  write_row(stream, obj, "switch", "<button>Toggle</button>");
213  }
214 #endif
215 
216 #ifdef USE_BUTTON
217  for (auto *obj : App.get_buttons())
218  write_row(stream, obj, "button", "<button>Press</button>");
219 #endif
220 
221 #ifdef USE_BINARY_SENSOR
222  for (auto *obj : App.get_binary_sensors()) {
223  if (this->include_internal_ || !obj->is_internal())
224  write_row(stream, obj, "binary_sensor", "");
225  }
226 #endif
227 
228 #ifdef USE_FAN
229  for (auto *obj : App.get_fans()) {
230  if (this->include_internal_ || !obj->is_internal())
231  write_row(stream, obj, "fan", "<button>Toggle</button>");
232  }
233 #endif
234 
235 #ifdef USE_LIGHT
236  for (auto *obj : App.get_lights()) {
237  if (this->include_internal_ || !obj->is_internal())
238  write_row(stream, obj, "light", "<button>Toggle</button>");
239  }
240 #endif
241 
242 #ifdef USE_TEXT_SENSOR
243  for (auto *obj : App.get_text_sensors()) {
244  if (this->include_internal_ || !obj->is_internal())
245  write_row(stream, obj, "text_sensor", "");
246  }
247 #endif
248 
249 #ifdef USE_COVER
250  for (auto *obj : App.get_covers()) {
251  if (this->include_internal_ || !obj->is_internal())
252  write_row(stream, obj, "cover", "<button>Open</button><button>Close</button>");
253  }
254 #endif
255 
256 #ifdef USE_NUMBER
257  for (auto *obj : App.get_numbers()) {
258  if (this->include_internal_ || !obj->is_internal()) {
259  write_row(stream, obj, "number", "", [](AsyncResponseStream &stream, EntityBase *obj) {
260  number::Number *number = (number::Number *) obj;
261  stream.print(R"(<input type="number" min=")");
262  stream.print(number->traits.get_min_value());
263  stream.print(R"(" max=")");
264  stream.print(number->traits.get_max_value());
265  stream.print(R"(" step=")");
266  stream.print(number->traits.get_step());
267  stream.print(R"(" value=")");
268  stream.print(number->state);
269  stream.print(R"("/>)");
270  });
271  }
272  }
273 #endif
274 
275 #ifdef USE_TEXT
276  for (auto *obj : App.get_texts()) {
277  if (this->include_internal_ || !obj->is_internal()) {
278  write_row(stream, obj, "text", "", [](AsyncResponseStream &stream, EntityBase *obj) {
279  text::Text *text = (text::Text *) obj;
280  auto mode = (int) text->traits.get_mode();
281  stream.print(R"(<input type=")");
282  if (mode == 2) {
283  stream.print(R"(password)");
284  } else { // default
285  stream.print(R"(text)");
286  }
287  stream.print(R"(" minlength=")");
288  stream.print(text->traits.get_min_length());
289  stream.print(R"(" maxlength=")");
290  stream.print(text->traits.get_max_length());
291  stream.print(R"(" pattern=")");
292  stream.print(text->traits.get_pattern().c_str());
293  stream.print(R"(" value=")");
294  stream.print(text->state.c_str());
295  stream.print(R"("/>)");
296  });
297  }
298  }
299 #endif
300 
301 #ifdef USE_SELECT
302  for (auto *obj : App.get_selects()) {
303  if (this->include_internal_ || !obj->is_internal()) {
304  write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, EntityBase *obj) {
305  select::Select *select = (select::Select *) obj;
306  stream.print("<select>");
307  stream.print("<option></option>");
308  for (auto const &option : select->traits.get_options()) {
309  stream.print("<option>");
310  stream.print(option.c_str());
311  stream.print("</option>");
312  }
313  stream.print("</select>");
314  });
315  }
316  }
317 #endif
318 
319 #ifdef USE_LOCK
320  for (auto *obj : App.get_locks()) {
321  if (this->include_internal_ || !obj->is_internal()) {
322  write_row(stream, obj, "lock", "", [](AsyncResponseStream &stream, EntityBase *obj) {
323  lock::Lock *lock = (lock::Lock *) obj;
324  stream.print("<button>Lock</button><button>Unlock</button>");
325  if (lock->traits.get_supports_open()) {
326  stream.print("<button>Open</button>");
327  }
328  });
329  }
330  }
331 #endif
332 
333 #ifdef USE_CLIMATE
334  for (auto *obj : App.get_climates()) {
335  if (this->include_internal_ || !obj->is_internal())
336  write_row(stream, obj, "climate", "");
337  }
338 #endif
339 
340  stream->print(F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/index.html\">ESPHome Web API</a> for "
341  "REST API documentation.</p>"));
342  if (this->allow_ota_) {
343  stream->print(
344  F("<h2>OTA Update</h2><form method=\"POST\" action=\"/update\" enctype=\"multipart/form-data\"><input "
345  "type=\"file\" name=\"update\"><input type=\"submit\" value=\"Update\"></form>"));
346  }
347  stream->print(F("<h2>Debug Log</h2><pre id=\"log\"></pre>"));
348 #ifdef USE_WEBSERVER_JS_INCLUDE
349  if (this->js_include_ != nullptr) {
350  stream->print(F("<script type=\"module\" src=\"/0.js\"></script>"));
351  }
352 #endif
353  if (strlen(this->js_url_) > 0) {
354  stream->print(F("<script src=\""));
355  stream->print(this->js_url_);
356  stream->print(F("\"></script>"));
357  }
358  stream->print(F("</article></body></html>"));
359  request->send(stream);
360 }
361 #elif USE_WEBSERVER_VERSION >= 2
362 void WebServer::handle_index_request(AsyncWebServerRequest *request) {
363  AsyncWebServerResponse *response =
364  request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
365  // No gzip header here because the HTML file is so small
366  request->send(response);
367 }
368 #endif
369 
370 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
371 void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) {
372  AsyncWebServerResponse *response = request->beginResponse(200, "");
373  response->addHeader(HEADER_CORS_ALLOW_PNA, "true");
374  response->addHeader(HEADER_PNA_NAME, App.get_name().c_str());
375  std::string mac = get_mac_address_pretty();
376  response->addHeader(HEADER_PNA_ID, mac.c_str());
377  request->send(response);
378 }
379 #endif
380 
381 #ifdef USE_WEBSERVER_CSS_INCLUDE
382 void WebServer::handle_css_request(AsyncWebServerRequest *request) {
383  AsyncWebServerResponse *response =
384  request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
385  response->addHeader("Content-Encoding", "gzip");
386  request->send(response);
387 }
388 #endif
389 
390 #ifdef USE_WEBSERVER_JS_INCLUDE
391 void WebServer::handle_js_request(AsyncWebServerRequest *request) {
392  AsyncWebServerResponse *response =
393  request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
394  response->addHeader("Content-Encoding", "gzip");
395  request->send(response);
396 }
397 #endif
398 
399 #define set_json_id(root, obj, sensor, start_config) \
400  (root)["id"] = sensor; \
401  if (((start_config) == DETAIL_ALL)) { \
402  (root)["name"] = (obj)->get_name(); \
403  (root)["icon"] = (obj)->get_icon(); \
404  (root)["entity_category"] = (obj)->get_entity_category(); \
405  if ((obj)->is_disabled_by_default()) \
406  (root)["is_disabled_by_default"] = (obj)->is_disabled_by_default(); \
407  }
408 
409 #define set_json_value(root, obj, sensor, value, start_config) \
410  set_json_id((root), (obj), sensor, start_config); \
411  (root)["value"] = value;
412 
413 #define set_json_icon_state_value(root, obj, sensor, state, value, start_config) \
414  set_json_value(root, obj, sensor, value, start_config); \
415  (root)["state"] = state;
416 
417 #ifdef USE_SENSOR
419  if (this->events_.count() == 0)
420  return;
421  this->events_.send(this->sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
422 }
423 void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
424  for (sensor::Sensor *obj : App.get_sensors()) {
425  if (obj->get_object_id() != match.id)
426  continue;
427  std::string data = this->sensor_json(obj, obj->state, DETAIL_STATE);
428  request->send(200, "application/json", data.c_str());
429  return;
430  }
431  request->send(404);
432 }
433 std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
434  return json::build_json([obj, value, start_config](JsonObject root) {
435  std::string state;
436  if (std::isnan(value)) {
437  state = "NA";
438  } else {
439  state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
440  if (!obj->get_unit_of_measurement().empty())
441  state += " " + obj->get_unit_of_measurement();
442  }
443  set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
444  if (start_config == DETAIL_ALL) {
445  if (!obj->get_unit_of_measurement().empty())
446  root["uom"] = obj->get_unit_of_measurement();
447  }
448  });
449 }
450 #endif
451 
452 #ifdef USE_TEXT_SENSOR
454  if (this->events_.count() == 0)
455  return;
456  this->events_.send(this->text_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
457 }
458 void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
460  if (obj->get_object_id() != match.id)
461  continue;
462  std::string data = this->text_sensor_json(obj, obj->state, DETAIL_STATE);
463  request->send(200, "application/json", data.c_str());
464  return;
465  }
466  request->send(404);
467 }
468 std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
469  JsonDetail start_config) {
470  return json::build_json([obj, value, start_config](JsonObject root) {
471  set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
472  });
473 }
474 #endif
475 
476 #ifdef USE_SWITCH
478  if (this->events_.count() == 0)
479  return;
480  this->events_.send(this->switch_json(obj, state, DETAIL_STATE).c_str(), "state");
481 }
482 std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
483  return json::build_json([obj, value, start_config](JsonObject root) {
484  set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
485  if (start_config == DETAIL_ALL) {
486  root["assumed_state"] = obj->assumed_state();
487  }
488  });
489 }
490 void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
491  for (switch_::Switch *obj : App.get_switches()) {
492  if (obj->get_object_id() != match.id)
493  continue;
494 
495  if (request->method() == HTTP_GET && match.method.empty()) {
496  std::string data = this->switch_json(obj, obj->state, DETAIL_STATE);
497  request->send(200, "application/json", data.c_str());
498  } else if (match.method == "toggle") {
499  this->schedule_([obj]() { obj->toggle(); });
500  request->send(200);
501  } else if (match.method == "turn_on") {
502  this->schedule_([obj]() { obj->turn_on(); });
503  request->send(200);
504  } else if (match.method == "turn_off") {
505  this->schedule_([obj]() { obj->turn_off(); });
506  request->send(200);
507  } else {
508  request->send(404);
509  }
510  return;
511  }
512  request->send(404);
513 }
514 #endif
515 
516 #ifdef USE_BUTTON
517 std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
518  return json::build_json(
519  [obj, start_config](JsonObject root) { set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); });
520 }
521 
522 void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
523  for (button::Button *obj : App.get_buttons()) {
524  if (obj->get_object_id() != match.id)
525  continue;
526  if (match.method == "press") {
527  this->schedule_([obj]() { obj->press(); });
528  request->send(200);
529  return;
530  } else {
531  request->send(404);
532  }
533  return;
534  }
535  request->send(404);
536 }
537 #endif
538 
539 #ifdef USE_BINARY_SENSOR
541  if (this->events_.count() == 0)
542  return;
543  this->events_.send(this->binary_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
544 }
545 std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
546  return json::build_json([obj, value, start_config](JsonObject root) {
547  set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
548  start_config);
549  });
550 }
551 void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
553  if (obj->get_object_id() != match.id)
554  continue;
555  std::string data = this->binary_sensor_json(obj, obj->state, DETAIL_STATE);
556  request->send(200, "application/json", data.c_str());
557  return;
558  }
559  request->send(404);
560 }
561 #endif
562 
563 #ifdef USE_FAN
565  if (this->events_.count() == 0)
566  return;
567  this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state");
568 }
569 std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
570  return json::build_json([obj, start_config](JsonObject root) {
571  set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
572  start_config);
573  const auto traits = obj->get_traits();
574  if (traits.supports_speed()) {
575  root["speed_level"] = obj->speed;
576  root["speed_count"] = traits.supported_speed_count();
577  }
578  if (obj->get_traits().supports_oscillation())
579  root["oscillation"] = obj->oscillating;
580  });
581 }
582 void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
583  for (fan::Fan *obj : App.get_fans()) {
584  if (obj->get_object_id() != match.id)
585  continue;
586 
587  if (request->method() == HTTP_GET && match.method.empty()) {
588  std::string data = this->fan_json(obj, DETAIL_STATE);
589  request->send(200, "application/json", data.c_str());
590  } else if (match.method == "toggle") {
591  this->schedule_([obj]() { obj->toggle().perform(); });
592  request->send(200);
593  } else if (match.method == "turn_on") {
594  auto call = obj->turn_on();
595  if (request->hasParam("speed_level")) {
596  auto speed_level = request->getParam("speed_level")->value();
597  auto val = parse_number<int>(speed_level.c_str());
598  if (!val.has_value()) {
599  ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
600  return;
601  }
602  call.set_speed(*val);
603  }
604  if (request->hasParam("oscillation")) {
605  auto speed = request->getParam("oscillation")->value();
606  auto val = parse_on_off(speed.c_str());
607  switch (val) {
608  case PARSE_ON:
609  call.set_oscillating(true);
610  break;
611  case PARSE_OFF:
612  call.set_oscillating(false);
613  break;
614  case PARSE_TOGGLE:
615  call.set_oscillating(!obj->oscillating);
616  break;
617  case PARSE_NONE:
618  request->send(404);
619  return;
620  }
621  }
622  this->schedule_([call]() mutable { call.perform(); });
623  request->send(200);
624  } else if (match.method == "turn_off") {
625  this->schedule_([obj]() { obj->turn_off().perform(); });
626  request->send(200);
627  } else {
628  request->send(404);
629  }
630  return;
631  }
632  request->send(404);
633 }
634 #endif
635 
636 #ifdef USE_LIGHT
638  if (this->events_.count() == 0)
639  return;
640  this->events_.send(this->light_json(obj, DETAIL_STATE).c_str(), "state");
641 }
642 void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
643  for (light::LightState *obj : App.get_lights()) {
644  if (obj->get_object_id() != match.id)
645  continue;
646 
647  if (request->method() == HTTP_GET && match.method.empty()) {
648  std::string data = this->light_json(obj, DETAIL_STATE);
649  request->send(200, "application/json", data.c_str());
650  } else if (match.method == "toggle") {
651  this->schedule_([obj]() { obj->toggle().perform(); });
652  request->send(200);
653  } else if (match.method == "turn_on") {
654  auto call = obj->turn_on();
655  if (request->hasParam("brightness")) {
656  auto brightness = parse_number<float>(request->getParam("brightness")->value().c_str());
657  if (brightness.has_value()) {
658  call.set_brightness(*brightness / 255.0f);
659  }
660  }
661  if (request->hasParam("r")) {
662  auto r = parse_number<float>(request->getParam("r")->value().c_str());
663  if (r.has_value()) {
664  call.set_red(*r / 255.0f);
665  }
666  }
667  if (request->hasParam("g")) {
668  auto g = parse_number<float>(request->getParam("g")->value().c_str());
669  if (g.has_value()) {
670  call.set_green(*g / 255.0f);
671  }
672  }
673  if (request->hasParam("b")) {
674  auto b = parse_number<float>(request->getParam("b")->value().c_str());
675  if (b.has_value()) {
676  call.set_blue(*b / 255.0f);
677  }
678  }
679  if (request->hasParam("white_value")) {
680  auto white_value = parse_number<float>(request->getParam("white_value")->value().c_str());
681  if (white_value.has_value()) {
682  call.set_white(*white_value / 255.0f);
683  }
684  }
685  if (request->hasParam("color_temp")) {
686  auto color_temp = parse_number<float>(request->getParam("color_temp")->value().c_str());
687  if (color_temp.has_value()) {
688  call.set_color_temperature(*color_temp);
689  }
690  }
691  if (request->hasParam("flash")) {
692  auto flash = parse_number<uint32_t>(request->getParam("flash")->value().c_str());
693  if (flash.has_value()) {
694  call.set_flash_length(*flash * 1000);
695  }
696  }
697  if (request->hasParam("transition")) {
698  auto transition = parse_number<uint32_t>(request->getParam("transition")->value().c_str());
699  if (transition.has_value()) {
700  call.set_transition_length(*transition * 1000);
701  }
702  }
703  if (request->hasParam("effect")) {
704  const char *effect = request->getParam("effect")->value().c_str();
705  call.set_effect(effect);
706  }
707 
708  this->schedule_([call]() mutable { call.perform(); });
709  request->send(200);
710  } else if (match.method == "turn_off") {
711  auto call = obj->turn_off();
712  if (request->hasParam("transition")) {
713  auto transition = parse_number<uint32_t>(request->getParam("transition")->value().c_str());
714  if (transition.has_value()) {
715  call.set_transition_length(*transition * 1000);
716  }
717  }
718  this->schedule_([call]() mutable { call.perform(); });
719  request->send(200);
720  } else {
721  request->send(404);
722  }
723  return;
724  }
725  request->send(404);
726 }
727 std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
728  return json::build_json([obj, start_config](JsonObject root) {
729  set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
730  root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
731 
733  if (start_config == DETAIL_ALL) {
734  JsonArray opt = root.createNestedArray("effects");
735  opt.add("None");
736  for (auto const &option : obj->get_effects()) {
737  opt.add(option->get_name());
738  }
739  }
740  });
741 }
742 #endif
743 
744 #ifdef USE_COVER
746  if (this->events_.count() == 0)
747  return;
748  this->events_.send(this->cover_json(obj, DETAIL_STATE).c_str(), "state");
749 }
750 void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
751  for (cover::Cover *obj : App.get_covers()) {
752  if (obj->get_object_id() != match.id)
753  continue;
754 
755  if (request->method() == HTTP_GET && match.method.empty()) {
756  std::string data = this->cover_json(obj, DETAIL_STATE);
757  request->send(200, "application/json", data.c_str());
758  continue;
759  }
760 
761  auto call = obj->make_call();
762  if (match.method == "open") {
763  call.set_command_open();
764  } else if (match.method == "close") {
765  call.set_command_close();
766  } else if (match.method == "stop") {
767  call.set_command_stop();
768  } else if (match.method == "toggle") {
769  call.set_command_toggle();
770  } else if (match.method != "set") {
771  request->send(404);
772  return;
773  }
774 
775  auto traits = obj->get_traits();
776  if ((request->hasParam("position") && !traits.get_supports_position()) ||
777  (request->hasParam("tilt") && !traits.get_supports_tilt())) {
778  request->send(409);
779  return;
780  }
781 
782  if (request->hasParam("position")) {
783  auto position = parse_number<float>(request->getParam("position")->value().c_str());
784  if (position.has_value()) {
785  call.set_position(*position);
786  }
787  }
788  if (request->hasParam("tilt")) {
789  auto tilt = parse_number<float>(request->getParam("tilt")->value().c_str());
790  if (tilt.has_value()) {
791  call.set_tilt(*tilt);
792  }
793  }
794 
795  this->schedule_([call]() mutable { call.perform(); });
796  request->send(200);
797  return;
798  }
799  request->send(404);
800 }
801 std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
802  return json::build_json([obj, start_config](JsonObject root) {
803  set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
804  obj->position, start_config);
805  root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
806 
807  if (obj->get_traits().get_supports_position())
808  root["position"] = obj->position;
809  if (obj->get_traits().get_supports_tilt())
810  root["tilt"] = obj->tilt;
811  });
812 }
813 #endif
814 
815 #ifdef USE_NUMBER
817  if (this->events_.count() == 0)
818  return;
819  this->events_.send(this->number_json(obj, state, DETAIL_STATE).c_str(), "state");
820 }
821 void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
822  for (auto *obj : App.get_numbers()) {
823  if (obj->get_object_id() != match.id)
824  continue;
825 
826  if (request->method() == HTTP_GET && match.method.empty()) {
827  std::string data = this->number_json(obj, obj->state, DETAIL_STATE);
828  request->send(200, "application/json", data.c_str());
829  return;
830  }
831  if (match.method != "set") {
832  request->send(404);
833  return;
834  }
835 
836  auto call = obj->make_call();
837  if (request->hasParam("value")) {
838  auto value = parse_number<float>(request->getParam("value")->value().c_str());
839  if (value.has_value())
840  call.set_value(*value);
841  }
842 
843  this->schedule_([call]() mutable { call.perform(); });
844  request->send(200);
845  return;
846  }
847  request->send(404);
848 }
849 
850 std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
851  return json::build_json([obj, value, start_config](JsonObject root) {
852  set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
853  if (start_config == DETAIL_ALL) {
854  root["min_value"] =
856  root["max_value"] =
858  root["step"] =
860  root["mode"] = (int) obj->traits.get_mode();
861  if (!obj->traits.get_unit_of_measurement().empty())
862  root["uom"] = obj->traits.get_unit_of_measurement();
863  }
864  if (std::isnan(value)) {
865  root["value"] = "\"NaN\"";
866  root["state"] = "NA";
867  } else {
868  root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
870  if (!obj->traits.get_unit_of_measurement().empty())
871  state += " " + obj->traits.get_unit_of_measurement();
872  root["state"] = state;
873  }
874  });
875 }
876 #endif
877 
878 #ifdef USE_DATETIME_DATE
880  if (this->events_.count() == 0)
881  return;
882  this->events_.send(this->date_json(obj, DETAIL_STATE).c_str(), "state");
883 }
884 void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
885  for (auto *obj : App.get_dates()) {
886  if (obj->get_object_id() != match.id)
887  continue;
888  if (request->method() == HTTP_GET) {
889  std::string data = this->date_json(obj, DETAIL_STATE);
890  request->send(200, "application/json", data.c_str());
891  return;
892  }
893  if (match.method != "set") {
894  request->send(404);
895  return;
896  }
897 
898  auto call = obj->make_call();
899 
900  if (!request->hasParam("value")) {
901  request->send(409);
902  return;
903  }
904 
905  if (request->hasParam("value")) {
906  std::string value = request->getParam("value")->value().c_str();
907  call.set_date(value);
908  }
909 
910  this->schedule_([call]() mutable { call.perform(); });
911  request->send(200);
912  return;
913  }
914  request->send(404);
915 }
916 
917 std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
918  return json::build_json([obj, start_config](JsonObject root) {
919  set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
920  std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
921  root["value"] = value;
922  root["state"] = value;
923  });
924 }
925 #endif // USE_DATETIME_DATE
926 
927 #ifdef USE_DATETIME_TIME
929  this->events_.send(this->time_json(obj, DETAIL_STATE).c_str(), "state");
930 }
931 void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
932  for (auto *obj : App.get_times()) {
933  if (obj->get_object_id() != match.id)
934  continue;
935  if (request->method() == HTTP_GET && match.method.empty()) {
936  std::string data = this->time_json(obj, DETAIL_STATE);
937  request->send(200, "application/json", data.c_str());
938  return;
939  }
940  if (match.method != "set") {
941  request->send(404);
942  return;
943  }
944 
945  auto call = obj->make_call();
946 
947  if (!request->hasParam("value")) {
948  request->send(409);
949  return;
950  }
951 
952  if (request->hasParam("value")) {
953  std::string value = request->getParam("value")->value().c_str();
954  call.set_time(value);
955  }
956 
957  this->schedule_([call]() mutable { call.perform(); });
958  request->send(200);
959  return;
960  }
961  request->send(404);
962 }
963 std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
964  return json::build_json([obj, start_config](JsonObject root) {
965  set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
966  std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
967  root["value"] = value;
968  root["state"] = value;
969  });
970 }
971 #endif // USE_DATETIME_TIME
972 
973 #ifdef USE_TEXT
974 void WebServer::on_text_update(text::Text *obj, const std::string &state) {
975  if (this->events_.count() == 0)
976  return;
977  this->events_.send(this->text_json(obj, state, DETAIL_STATE).c_str(), "state");
978 }
979 void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
980  for (auto *obj : App.get_texts()) {
981  if (obj->get_object_id() != match.id)
982  continue;
983 
984  if (request->method() == HTTP_GET && match.method.empty()) {
985  std::string data = this->text_json(obj, obj->state, DETAIL_STATE);
986  request->send(200, "text/json", data.c_str());
987  return;
988  }
989  if (match.method != "set") {
990  request->send(404);
991  return;
992  }
993 
994  auto call = obj->make_call();
995  if (request->hasParam("value")) {
996  String value = request->getParam("value")->value();
997  call.set_value(value.c_str());
998  }
999 
1000  this->defer([call]() mutable { call.perform(); });
1001  request->send(200);
1002  return;
1003  }
1004  request->send(404);
1005 }
1006 
1007 std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
1008  return json::build_json([obj, value, start_config](JsonObject root) {
1009  set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
1010  if (start_config == DETAIL_ALL) {
1011  root["mode"] = (int) obj->traits.get_mode();
1012  }
1013  root["min_length"] = obj->traits.get_min_length();
1014  root["max_length"] = obj->traits.get_max_length();
1015  root["pattern"] = obj->traits.get_pattern();
1017  root["state"] = "********";
1018  } else {
1019  root["state"] = value;
1020  }
1021  root["value"] = value;
1022  });
1023 }
1024 #endif
1025 
1026 #ifdef USE_SELECT
1027 void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
1028  if (this->events_.count() == 0)
1029  return;
1030  this->events_.send(this->select_json(obj, state, DETAIL_STATE).c_str(), "state");
1031 }
1032 void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1033  for (auto *obj : App.get_selects()) {
1034  if (obj->get_object_id() != match.id)
1035  continue;
1036 
1037  if (request->method() == HTTP_GET && match.method.empty()) {
1038  auto detail = DETAIL_STATE;
1039  auto *param = request->getParam("detail");
1040  if (param && param->value() == "all") {
1041  detail = DETAIL_ALL;
1042  }
1043  std::string data = this->select_json(obj, obj->state, detail);
1044  request->send(200, "application/json", data.c_str());
1045  return;
1046  }
1047 
1048  if (match.method != "set") {
1049  request->send(404);
1050  return;
1051  }
1052 
1053  auto call = obj->make_call();
1054 
1055  if (request->hasParam("option")) {
1056  auto option = request->getParam("option")->value();
1057  call.set_option(option.c_str()); // NOLINT(clang-diagnostic-deprecated-declarations)
1058  }
1059 
1060  this->schedule_([call]() mutable { call.perform(); });
1061  request->send(200);
1062  return;
1063  }
1064  request->send(404);
1065 }
1066 std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
1067  return json::build_json([obj, value, start_config](JsonObject root) {
1068  set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
1069  if (start_config == DETAIL_ALL) {
1070  JsonArray opt = root.createNestedArray("option");
1071  for (auto &option : obj->traits.get_options()) {
1072  opt.add(option);
1073  }
1074  }
1075  });
1076 }
1077 #endif
1078 
1079 // Longest: HORIZONTAL
1080 #define PSTR_LOCAL(mode_s) strncpy_P(buf, (PGM_P) ((mode_s)), 15)
1081 
1082 #ifdef USE_CLIMATE
1084  if (this->events_.count() == 0)
1085  return;
1086  this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
1087 }
1088 
1089 void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1090  for (auto *obj : App.get_climates()) {
1091  if (obj->get_object_id() != match.id)
1092  continue;
1093 
1094  if (request->method() == HTTP_GET && match.method.empty()) {
1095  std::string data = this->climate_json(obj, DETAIL_STATE);
1096  request->send(200, "application/json", data.c_str());
1097  return;
1098  }
1099 
1100  if (match.method != "set") {
1101  request->send(404);
1102  return;
1103  }
1104 
1105  auto call = obj->make_call();
1106 
1107  if (request->hasParam("mode")) {
1108  auto mode = request->getParam("mode")->value();
1109  call.set_mode(mode.c_str());
1110  }
1111 
1112  if (request->hasParam("target_temperature_high")) {
1113  auto target_temperature_high = parse_number<float>(request->getParam("target_temperature_high")->value().c_str());
1114  if (target_temperature_high.has_value())
1115  call.set_target_temperature_high(*target_temperature_high);
1116  }
1117 
1118  if (request->hasParam("target_temperature_low")) {
1119  auto target_temperature_low = parse_number<float>(request->getParam("target_temperature_low")->value().c_str());
1120  if (target_temperature_low.has_value())
1121  call.set_target_temperature_low(*target_temperature_low);
1122  }
1123 
1124  if (request->hasParam("target_temperature")) {
1125  auto target_temperature = parse_number<float>(request->getParam("target_temperature")->value().c_str());
1126  if (target_temperature.has_value())
1127  call.set_target_temperature(*target_temperature);
1128  }
1129 
1130  this->schedule_([call]() mutable { call.perform(); });
1131  request->send(200);
1132  return;
1133  }
1134  request->send(404);
1135 }
1136 
1137 std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
1138  return json::build_json([obj, start_config](JsonObject root) {
1139  set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
1140  const auto traits = obj->get_traits();
1141  int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
1142  int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
1143  char buf[16];
1144 
1145  if (start_config == DETAIL_ALL) {
1146  JsonArray opt = root.createNestedArray("modes");
1147  for (climate::ClimateMode m : traits.get_supported_modes())
1148  opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
1149  if (!traits.get_supported_custom_fan_modes().empty()) {
1150  JsonArray opt = root.createNestedArray("fan_modes");
1151  for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
1152  opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
1153  }
1154 
1155  if (!traits.get_supported_custom_fan_modes().empty()) {
1156  JsonArray opt = root.createNestedArray("custom_fan_modes");
1157  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
1158  opt.add(custom_fan_mode);
1159  }
1160  if (traits.get_supports_swing_modes()) {
1161  JsonArray opt = root.createNestedArray("swing_modes");
1162  for (auto swing_mode : traits.get_supported_swing_modes())
1163  opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
1164  }
1165  if (traits.get_supports_presets() && obj->preset.has_value()) {
1166  JsonArray opt = root.createNestedArray("presets");
1167  for (climate::ClimatePreset m : traits.get_supported_presets())
1168  opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
1169  }
1170  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1171  JsonArray opt = root.createNestedArray("custom_presets");
1172  for (auto const &custom_preset : traits.get_supported_custom_presets())
1173  opt.add(custom_preset);
1174  }
1175  }
1176 
1177  bool has_state = false;
1178  root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
1179  root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
1180  root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
1181  root["step"] = traits.get_visual_target_temperature_step();
1182  if (traits.get_supports_action()) {
1183  root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
1184  root["state"] = root["action"];
1185  has_state = true;
1186  }
1187  if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
1188  root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
1189  }
1190  if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
1191  root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
1192  }
1193  if (traits.get_supports_presets() && obj->preset.has_value()) {
1194  root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
1195  }
1196  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1197  root["custom_preset"] = obj->custom_preset.value().c_str();
1198  }
1199  if (traits.get_supports_swing_modes()) {
1200  root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
1201  }
1202  if (traits.get_supports_current_temperature()) {
1203  if (!std::isnan(obj->current_temperature)) {
1204  root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
1205  } else {
1206  root["current_temperature"] = "NA";
1207  }
1208  }
1209  if (traits.get_supports_two_point_target_temperature()) {
1210  root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
1211  root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
1212  if (!has_state) {
1213  root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
1214  target_accuracy);
1215  }
1216  } else {
1217  root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
1218  if (!has_state)
1219  root["state"] = root["target_temperature"];
1220  }
1221  });
1222 }
1223 #endif
1224 
1225 #ifdef USE_LOCK
1227  if (this->events_.count() == 0)
1228  return;
1229  this->events_.send(this->lock_json(obj, obj->state, DETAIL_STATE).c_str(), "state");
1230 }
1231 std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
1232  return json::build_json([obj, value, start_config](JsonObject root) {
1233  set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
1234  start_config);
1235  });
1236 }
1237 void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1238  for (lock::Lock *obj : App.get_locks()) {
1239  if (obj->get_object_id() != match.id)
1240  continue;
1241 
1242  if (request->method() == HTTP_GET && match.method.empty()) {
1243  std::string data = this->lock_json(obj, obj->state, DETAIL_STATE);
1244  request->send(200, "application/json", data.c_str());
1245  } else if (match.method == "lock") {
1246  this->schedule_([obj]() { obj->lock(); });
1247  request->send(200);
1248  } else if (match.method == "unlock") {
1249  this->schedule_([obj]() { obj->unlock(); });
1250  request->send(200);
1251  } else if (match.method == "open") {
1252  this->schedule_([obj]() { obj->open(); });
1253  request->send(200);
1254  } else {
1255  request->send(404);
1256  }
1257  return;
1258  }
1259  request->send(404);
1260 }
1261 #endif
1262 
1263 #ifdef USE_ALARM_CONTROL_PANEL
1265  if (this->events_.count() == 0)
1266  return;
1267  this->events_.send(this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE).c_str(), "state");
1268 }
1271  JsonDetail start_config) {
1272  return json::build_json([obj, value, start_config](JsonObject root) {
1273  char buf[16];
1274  set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
1275  PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
1276  });
1277 }
1278 void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1280  if (obj->get_object_id() != match.id)
1281  continue;
1282 
1283  if (request->method() == HTTP_GET && match.method.empty()) {
1284  std::string data = this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE);
1285  request->send(200, "application/json", data.c_str());
1286  return;
1287  }
1288  }
1289  request->send(404);
1290 }
1291 #endif
1292 
1293 bool WebServer::canHandle(AsyncWebServerRequest *request) {
1294  if (request->url() == "/")
1295  return true;
1296 
1297 #ifdef USE_WEBSERVER_CSS_INCLUDE
1298  if (request->url() == "/0.css")
1299  return true;
1300 #endif
1301 
1302 #ifdef USE_WEBSERVER_JS_INCLUDE
1303  if (request->url() == "/0.js")
1304  return true;
1305 #endif
1306 
1307 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
1308  if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
1309 #ifdef USE_ARDUINO
1310  // Header needs to be added to interesting header list for it to not be
1311  // nuked by the time we handle the request later.
1312  // Only required in Arduino framework.
1313  request->addInterestingHeader(HEADER_CORS_REQ_PNA);
1314 #endif
1315  return true;
1316  }
1317 #endif
1318 
1319  UrlMatch match = match_url(request->url().c_str(), true);
1320  if (!match.valid)
1321  return false;
1322 #ifdef USE_SENSOR
1323  if (request->method() == HTTP_GET && match.domain == "sensor")
1324  return true;
1325 #endif
1326 
1327 #ifdef USE_SWITCH
1328  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "switch")
1329  return true;
1330 #endif
1331 
1332 #ifdef USE_BUTTON
1333  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "button")
1334  return true;
1335 #endif
1336 
1337 #ifdef USE_BINARY_SENSOR
1338  if (request->method() == HTTP_GET && match.domain == "binary_sensor")
1339  return true;
1340 #endif
1341 
1342 #ifdef USE_FAN
1343  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "fan")
1344  return true;
1345 #endif
1346 
1347 #ifdef USE_LIGHT
1348  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "light")
1349  return true;
1350 #endif
1351 
1352 #ifdef USE_TEXT_SENSOR
1353  if (request->method() == HTTP_GET && match.domain == "text_sensor")
1354  return true;
1355 #endif
1356 
1357 #ifdef USE_COVER
1358  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "cover")
1359  return true;
1360 #endif
1361 
1362 #ifdef USE_NUMBER
1363  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "number")
1364  return true;
1365 #endif
1366 
1367 #ifdef USE_DATETIME_DATE
1368  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "date")
1369  return true;
1370 #endif
1371 
1372 #ifdef USE_DATETIME_TIME
1373  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "time")
1374  return true;
1375 #endif
1376 
1377 #ifdef USE_TEXT
1378  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text")
1379  return true;
1380 #endif
1381 
1382 #ifdef USE_SELECT
1383  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "select")
1384  return true;
1385 #endif
1386 
1387 #ifdef USE_CLIMATE
1388  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "climate")
1389  return true;
1390 #endif
1391 
1392 #ifdef USE_LOCK
1393  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "lock")
1394  return true;
1395 #endif
1396 
1397 #ifdef USE_ALARM_CONTROL_PANEL
1398  if (request->method() == HTTP_GET && match.domain == "alarm_control_panel")
1399  return true;
1400 #endif
1401 
1402  return false;
1403 }
1404 void WebServer::handleRequest(AsyncWebServerRequest *request) {
1405  if (request->url() == "/") {
1406  this->handle_index_request(request);
1407  return;
1408  }
1409 
1410 #ifdef USE_WEBSERVER_CSS_INCLUDE
1411  if (request->url() == "/0.css") {
1412  this->handle_css_request(request);
1413  return;
1414  }
1415 #endif
1416 
1417 #ifdef USE_WEBSERVER_JS_INCLUDE
1418  if (request->url() == "/0.js") {
1419  this->handle_js_request(request);
1420  return;
1421  }
1422 #endif
1423 
1424 #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
1425  if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
1426  this->handle_pna_cors_request(request);
1427  return;
1428  }
1429 #endif
1430 
1431  UrlMatch match = match_url(request->url().c_str());
1432 #ifdef USE_SENSOR
1433  if (match.domain == "sensor") {
1434  this->handle_sensor_request(request, match);
1435  return;
1436  }
1437 #endif
1438 
1439 #ifdef USE_SWITCH
1440  if (match.domain == "switch") {
1441  this->handle_switch_request(request, match);
1442  return;
1443  }
1444 #endif
1445 
1446 #ifdef USE_BUTTON
1447  if (match.domain == "button") {
1448  this->handle_button_request(request, match);
1449  return;
1450  }
1451 #endif
1452 
1453 #ifdef USE_BINARY_SENSOR
1454  if (match.domain == "binary_sensor") {
1455  this->handle_binary_sensor_request(request, match);
1456  return;
1457  }
1458 #endif
1459 
1460 #ifdef USE_FAN
1461  if (match.domain == "fan") {
1462  this->handle_fan_request(request, match);
1463  return;
1464  }
1465 #endif
1466 
1467 #ifdef USE_LIGHT
1468  if (match.domain == "light") {
1469  this->handle_light_request(request, match);
1470  return;
1471  }
1472 #endif
1473 
1474 #ifdef USE_TEXT_SENSOR
1475  if (match.domain == "text_sensor") {
1476  this->handle_text_sensor_request(request, match);
1477  return;
1478  }
1479 #endif
1480 
1481 #ifdef USE_COVER
1482  if (match.domain == "cover") {
1483  this->handle_cover_request(request, match);
1484  return;
1485  }
1486 #endif
1487 
1488 #ifdef USE_NUMBER
1489  if (match.domain == "number") {
1490  this->handle_number_request(request, match);
1491  return;
1492  }
1493 #endif
1494 
1495 #ifdef USE_DATETIME_DATE
1496  if (match.domain == "date") {
1497  this->handle_date_request(request, match);
1498  return;
1499  }
1500 #endif
1501 
1502 #ifdef USE_DATETIME_TIME
1503  if (match.domain == "time") {
1504  this->handle_time_request(request, match);
1505  return;
1506  }
1507 #endif
1508 
1509 #ifdef USE_TEXT
1510  if (match.domain == "text") {
1511  this->handle_text_request(request, match);
1512  return;
1513  }
1514 #endif
1515 
1516 #ifdef USE_SELECT
1517  if (match.domain == "select") {
1518  this->handle_select_request(request, match);
1519  return;
1520  }
1521 #endif
1522 
1523 #ifdef USE_CLIMATE
1524  if (match.domain == "climate") {
1525  this->handle_climate_request(request, match);
1526  return;
1527  }
1528 #endif
1529 
1530 #ifdef USE_LOCK
1531  if (match.domain == "lock") {
1532  this->handle_lock_request(request, match);
1533 
1534  return;
1535  }
1536 #endif
1537 
1538 #ifdef USE_ALARM_CONTROL_PANEL
1539  if (match.domain == "alarm_control_panel") {
1540  this->handle_alarm_control_panel_request(request, match);
1541 
1542  return;
1543  }
1544 #endif
1545 }
1546 
1547 bool WebServer::isRequestHandlerTrivial() { return false; }
1548 
1549 void WebServer::schedule_(std::function<void()> &&f) {
1550 #ifdef USE_ESP32
1551  xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);
1552  to_schedule_.push_back(std::move(f));
1553  xSemaphoreGive(this->to_schedule_lock_);
1554 #else
1555  this->defer(std::move(f));
1556 #endif
1557 }
1558 
1559 } // namespace web_server
1560 } // namespace esphome
Base class for all switches.
Definition: switch.h:39
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:585
void handle_pna_cors_request(AsyncWebServerRequest *request)
Definition: web_server.cpp:371
AlarmControlPanelState get_state() const
Get the state.
void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a number request under &#39;/number/<id>&#39;.
Definition: web_server.cpp:821
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition: light_state.h:34
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:850
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:433
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:159
void on_sensor_update(sensor::Sensor *obj, float state) override
Definition: web_server.cpp:418
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:412
WebServer(web_server_base::WebServerBase *base)
Definition: web_server.cpp:94
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
std::string state
Definition: text.h:26
void handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a time request under &#39;/time/<id>&#39;.
Definition: web_server.cpp:931
const std::vector< climate::Climate * > & get_climates()
Definition: application.h:289
float target_temperature
The target temperature of the climate device.
Definition: climate.h:186
SemaphoreHandle_t to_schedule_lock_
Definition: web_server.h:318
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:551
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.
LockState state
The current reported state of the lock.
Definition: lock.h:122
const std::vector< alarm_control_panel::AlarmControlPanel * > & get_alarm_control_panels()
Definition: application.h:362
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
void handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a text input request under &#39;/text/<id>&#39;.
Definition: web_server.cpp:979
CoverOperation current_operation
The current operation of the cover (idle, opening, closing).
Definition: cover.h:116
Base class for all buttons.
Definition: button.h:29
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
void defer(const std::string &name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition: component.cpp:130
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
int speed
Definition: fan.h:35
virtual bool assumed_state()
Return whether this switch uses an assumed state - i.e.
Definition: switch.cpp:58
float tilt
Definition: cover.h:15
void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action, const std::function< void(AsyncResponseStream &stream, EntityBase *obj)> &action_func=nullptr)
Definition: web_server.cpp:46
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
Definition: application.h:177
void setup() override
Setup the internal web server and register handlers.
Definition: web_server.cpp:123
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:262
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override
Definition: web_server.cpp:540
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:642
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;.
bool has_value() const
Definition: optional.h:87
float target_temperature_high
Definition: climate.h:586
void handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a button request under &#39;/button/<id>/press&#39;.
Definition: web_server.cpp:522
int get_max_length() const
Definition: text_traits.h:21
Base-class for all text inputs.
Definition: text.h:24
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:637
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:316
std::string get_object_id() const
Definition: entity_base.cpp:43
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
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:397
ClimateSwingMode swing_mode
Definition: climate.h:581
const size_t ESPHOME_WEBSERVER_JS_INCLUDE_SIZE
Internal helper struct that is used to parse incoming URLs.
Definition: web_server.h:35
LockTraits traits
Definition: lock.h:124
optional< std::string > custom_fan_mode
The active custom fan mode of the climate device.
Definition: climate.h:205
virtual CoverTraits get_traits()=0
void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a sensor request under &#39;/sensor/<id>&#39;.
Definition: web_server.cpp:423
const std::vector< lock::Lock * > & get_locks()
Definition: application.h:343
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:468
const size_t ESPHOME_WEBSERVER_INDEX_HTML_SIZE
std::string domain
The domain of the component, for example "sensor".
Definition: web_server.h:36
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.
Logger * global_logger
Definition: logger.cpp:179
uint8_t m
Definition: bl0939.h:20
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override
Definition: web_server.cpp:453
const char *const TAG
Definition: spi.cpp:8
void add_handler(AsyncWebHandler *handler)
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:107
void on_text_update(text::Text *obj, const std::string &state) override
Definition: web_server.cpp:974
const std::vector< button::Button * > & get_buttons()
Definition: application.h:235
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:490
const LogString * alarm_control_panel_state_to_string(AlarmControlPanelState state)
Returns a string representation of the state.
std::vector< std::string > get_options() const
optional< ClimatePreset > preset
The active preset of the climate device.
Definition: climate.h:208
uint8_t custom_preset
Definition: climate.h:579
UrlMatch match_url(const std::string &url, bool only_domain=false)
Definition: web_server.cpp:68
const std::vector< switch_::Switch * > & get_switches()
Definition: application.h:226
Base-class for all numbers.
Definition: number.h:39
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:312
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:382
bool valid
Whether this match is valid.
Definition: web_server.h:39
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:151
bool is_internal() const
Definition: entity_base.cpp:22
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.
Definition: web_server.cpp:963
const std::vector< text_sensor::TextSensor * > & get_text_sensors()
Definition: application.h:253
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:244
Application App
Global storage of Application pointer - only one Application can exist.
const std::vector< binary_sensor::BinarySensor * > & get_binary_sensors()
Definition: application.h:217
const std::vector< LightEffect * > & get_effects() const
Get all effects for this light state.
int8_t step_to_accuracy_decimals(float step)
Derive accuracy in decimals from an increment step.
Definition: helpers.cpp:423
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:482
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition: json_util.cpp:21
void begin(bool include_internal=false)
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:174
std::string light_json(light::LightState *obj, JsonDetail start_config)
Dump the light state as a JSON string.
Definition: web_server.cpp:727
static void dump_json(LightState &state, JsonObject root)
Dump the state of a light as JSON.
const std::vector< text::Text * > & get_texts()
Definition: application.h:325
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:175
NumberTraits traits
Definition: number.h:49
void on_time_update(datetime::TimeEntity *obj) override
Definition: web_server.cpp:928
void on_climate_update(climate::Climate *obj) override
const std::vector< cover::Cover * > & get_covers()
Definition: application.h:271
constexpr const char * c_str() const
Definition: string_ref.h:68
void set_js_url(const char *js_url)
Set the URL to the script that&#39;s embedded in the index page.
Definition: web_server.cpp:103
float get_setup_priority() const override
MQTT setup priority.
Definition: web_server.cpp:172
optional< std::string > custom_preset
The active custom preset mode of the climate device.
Definition: climate.h:211
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:582
void set_css_url(const char *css_url)
Set the URL to the CSS <link> that&#39;s sent to each client.
Definition: web_server.cpp:102
std::string id
The id of the device that&#39;s being accessed, for example "living_room_fan".
Definition: web_server.h:37
const std::vector< light::LightState * > & get_lights()
Definition: application.h:280
void on_date_update(datetime::DateEntity *obj) override
Definition: web_server.cpp:879
std::string get_comment() const
Get the comment of this Application set by pre_setup().
Definition: application.h:183
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:517
void on_cover_update(cover::Cover *obj) override
Definition: web_server.cpp:745
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:750
void on_switch_update(switch_::Switch *obj, bool state) override
Definition: web_server.cpp:477
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
std::string date_json(datetime::DateEntity *obj, JsonDetail start_config)
Dump the date state with its value as a JSON string.
Definition: web_server.cpp:917
std::string get_config_json()
Return the webserver configuration as JSON.
Definition: web_server.cpp:113
Base-class for all selects.
Definition: select.h:31
void on_fan_update(fan::Fan *obj) override
Definition: web_server.cpp:564
web_server_base::WebServerBase * base_
Definition: web_server.h:300
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void on_number_update(number::Number *obj, float state) override
Definition: web_server.cpp:816
std::string fan_json(fan::Fan *obj, JsonDetail start_config)
Dump the fan state as a JSON string.
Definition: web_server.cpp:569
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:77
LockState
Enum for all states a lock can be in.
Definition: lock.h:26
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:307
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:334
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:592
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.
ListEntitiesIterator entities_iterator_
Definition: web_server.h:302
std::deque< std::function< void()> > to_schedule_
Definition: web_server.h:317
const std::vector< number::Number * > & get_numbers()
Definition: application.h:298
const LogString * climate_action_to_string(ClimateAction action)
Convert the given ClimateAction to a human-readable string.
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:458
uint8_t custom_fan_mode
Definition: climate.h:574
bool get_supports_open() const
Definition: lock.h:40
float target_temperature
Definition: climate.h:583
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
const StringRef & get_name() const
Definition: entity_base.cpp:10
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:38
void handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a date request under &#39;/date/<id>&#39;.
Definition: web_server.cpp:884
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:545
bool state
Definition: fan.h:34
std::string cover_json(cover::Cover *obj, JsonDetail start_config)
Dump the cover state as a JSON string.
Definition: web_server.cpp:801
void handle_js_request(AsyncWebServerRequest *request)
Handle included js request under &#39;/0.js&#39;.
Definition: web_server.cpp:391
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:110
const LogString * climate_swing_mode_to_string(ClimateSwingMode swing_mode)
Convert the given ClimateSwingMode to a human-readable string.