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