ESPHome  2023.5.5
web_server.cpp
Go to the documentation of this file.
1 #ifdef USE_ARDUINO
2 
3 #include "web_server.h"
4 
9 #include "esphome/core/log.h"
10 #include "esphome/core/util.h"
11 
12 #include "StreamString.h"
13 
14 #include <cstdlib>
15 
16 #ifdef USE_LIGHT
18 #endif
19 
20 #ifdef USE_LOGGER
22 #endif
23 
24 #ifdef USE_CLIMATE
26 #endif
27 
28 #ifdef USE_WEBSERVER_LOCAL
29 #include "server_index.h"
30 #endif
31 
32 namespace esphome {
33 namespace web_server {
34 
35 static const char *const TAG = "web_server";
36 
37 #if USE_WEBSERVER_VERSION == 1
38 void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action,
39  const std::function<void(AsyncResponseStream &stream, EntityBase *obj)> &action_func = nullptr) {
40  stream->print("<tr class=\"");
41  stream->print(klass.c_str());
42  if (obj->is_internal())
43  stream->print(" internal");
44  stream->print("\" id=\"");
45  stream->print(klass.c_str());
46  stream->print("-");
47  stream->print(obj->get_object_id().c_str());
48  stream->print("\"><td>");
49  stream->print(obj->get_name().c_str());
50  stream->print("</td><td></td><td>");
51  stream->print(action.c_str());
52  if (action_func) {
53  action_func(*stream, obj);
54  }
55  stream->print("</td>");
56  stream->print("</tr>");
57 }
58 #endif
59 
60 UrlMatch match_url(const std::string &url, bool only_domain = false) {
61  UrlMatch match;
62  match.valid = false;
63  size_t domain_end = url.find('/', 1);
64  if (domain_end == std::string::npos)
65  return match;
66  match.domain = url.substr(1, domain_end - 1);
67  if (only_domain) {
68  match.valid = true;
69  return match;
70  }
71  if (url.length() == domain_end - 1)
72  return match;
73  size_t id_begin = domain_end + 1;
74  size_t id_end = url.find('/', id_begin);
75  match.valid = true;
76  if (id_end == std::string::npos) {
77  match.id = url.substr(id_begin, url.length() - id_begin);
78  return match;
79  }
80  match.id = url.substr(id_begin, id_end - id_begin);
81  size_t method_begin = id_end + 1;
82  match.method = url.substr(method_begin, url.length() - method_begin);
83  return match;
84 }
85 
87  : base_(base), entities_iterator_(ListEntitiesIterator(this)) {
88 #ifdef USE_ESP32
89  to_schedule_lock_ = xSemaphoreCreateMutex();
90 #endif
91 }
92 
93 void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; }
94 void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; }
95 void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; }
96 void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; }
97 
99  ESP_LOGCONFIG(TAG, "Setting up web server...");
100  this->setup_controller(this->include_internal_);
101  this->base_->init();
102 
103  this->events_.onConnect([this](AsyncEventSourceClient *client) {
104  // Configure reconnect timeout and send config
105 
106  client->send(json::build_json([this](JsonObject root) {
107  root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
108  root["comment"] = App.get_comment();
109  root["ota"] = this->allow_ota_;
110  root["lang"] = "en";
111  }).c_str(),
112  "ping", millis(), 30000);
113 
115  });
116 
117 #ifdef USE_LOGGER
118  if (logger::global_logger != nullptr) {
120  [this](int level, const char *tag, const char *message) { this->events_.send(message, "log", millis()); });
121  }
122 #endif
123  this->base_->add_handler(&this->events_);
124  this->base_->add_handler(this);
125 
126  if (this->allow_ota_)
127  this->base_->add_ota_handler();
128 
129  this->set_interval(10000, [this]() { this->events_.send("", "ping", millis(), 30000); });
130 }
132 #ifdef USE_ESP32
133  if (xSemaphoreTake(this->to_schedule_lock_, 0L)) {
134  std::function<void()> fn;
135  if (!to_schedule_.empty()) {
136  // scheduler execute things out of order which may lead to incorrect state
137  // this->defer(std::move(to_schedule_.front()));
138  // let's execute it directly from the loop
139  fn = std::move(to_schedule_.front());
140  to_schedule_.pop_front();
141  }
142  xSemaphoreGive(this->to_schedule_lock_);
143  if (fn) {
144  fn();
145  }
146  }
147 #endif
148  this->entities_iterator_.advance();
149 }
151  ESP_LOGCONFIG(TAG, "Web Server:");
152  ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port());
153 }
154 float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; }
155 
156 #ifdef USE_WEBSERVER_LOCAL
157 void WebServer::handle_index_request(AsyncWebServerRequest *request) {
158  AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
159  response->addHeader("Content-Encoding", "gzip");
160  request->send(response);
161 }
162 #else
163 void WebServer::handle_index_request(AsyncWebServerRequest *request) {
164  AsyncResponseStream *stream = request->beginResponseStream("text/html");
165  // All content is controlled and created by user - so allowing all origins is fine here.
166  stream->addHeader("Access-Control-Allow-Origin", "*");
167 #if USE_WEBSERVER_VERSION == 1
168  const std::string &title = App.get_name();
169  stream->print(F("<!DOCTYPE html><html lang=\"en\"><head><meta charset=UTF-8><meta "
170  "name=viewport content=\"width=device-width, initial-scale=1,user-scalable=no\"><title>"));
171  stream->print(title.c_str());
172  stream->print(F("</title>"));
173 #else
174  stream->print(F("<!DOCTYPE html><html><head><meta charset=UTF-8><link rel=icon href=data:>"));
175 #endif
176 #ifdef USE_WEBSERVER_CSS_INCLUDE
177  stream->print(F("<link rel=\"stylesheet\" href=\"/0.css\">"));
178 #endif
179  if (strlen(this->css_url_) > 0) {
180  stream->print(F("<link rel=\"stylesheet\" href=\""));
181  stream->print(this->css_url_);
182  stream->print(F("\">"));
183  }
184  stream->print(F("</head><body>"));
185 #if USE_WEBSERVER_VERSION == 1
186  stream->print(F("<article class=\"markdown-body\"><h1>"));
187  stream->print(title.c_str());
188  stream->print(F("</h1>"));
189  stream->print(F("<h2>States</h2><table id=\"states\"><thead><tr><th>Name<th>State<th>Actions<tbody>"));
190 
191 #ifdef USE_SENSOR
192  for (auto *obj : App.get_sensors()) {
193  if (this->include_internal_ || !obj->is_internal())
194  write_row(stream, obj, "sensor", "");
195  }
196 #endif
197 
198 #ifdef USE_SWITCH
199  for (auto *obj : App.get_switches()) {
200  if (this->include_internal_ || !obj->is_internal())
201  write_row(stream, obj, "switch", "<button>Toggle</button>");
202  }
203 #endif
204 
205 #ifdef USE_BUTTON
206  for (auto *obj : App.get_buttons())
207  write_row(stream, obj, "button", "<button>Press</button>");
208 #endif
209 
210 #ifdef USE_BINARY_SENSOR
211  for (auto *obj : App.get_binary_sensors()) {
212  if (this->include_internal_ || !obj->is_internal())
213  write_row(stream, obj, "binary_sensor", "");
214  }
215 #endif
216 
217 #ifdef USE_FAN
218  for (auto *obj : App.get_fans()) {
219  if (this->include_internal_ || !obj->is_internal())
220  write_row(stream, obj, "fan", "<button>Toggle</button>");
221  }
222 #endif
223 
224 #ifdef USE_LIGHT
225  for (auto *obj : App.get_lights()) {
226  if (this->include_internal_ || !obj->is_internal())
227  write_row(stream, obj, "light", "<button>Toggle</button>");
228  }
229 #endif
230 
231 #ifdef USE_TEXT_SENSOR
232  for (auto *obj : App.get_text_sensors()) {
233  if (this->include_internal_ || !obj->is_internal())
234  write_row(stream, obj, "text_sensor", "");
235  }
236 #endif
237 
238 #ifdef USE_COVER
239  for (auto *obj : App.get_covers()) {
240  if (this->include_internal_ || !obj->is_internal())
241  write_row(stream, obj, "cover", "<button>Open</button><button>Close</button>");
242  }
243 #endif
244 
245 #ifdef USE_NUMBER
246  for (auto *obj : App.get_numbers()) {
247  if (this->include_internal_ || !obj->is_internal()) {
248  write_row(stream, obj, "number", "", [](AsyncResponseStream &stream, EntityBase *obj) {
249  number::Number *number = (number::Number *) obj;
250  stream.print(R"(<input type="number" min=")");
251  stream.print(number->traits.get_min_value());
252  stream.print(R"(" max=")");
253  stream.print(number->traits.get_max_value());
254  stream.print(R"(" step=")");
255  stream.print(number->traits.get_step());
256  stream.print(R"(" value=")");
257  stream.print(number->state);
258  stream.print(R"("/>)");
259  });
260  }
261  }
262 #endif
263 
264 #ifdef USE_SELECT
265  for (auto *obj : App.get_selects()) {
266  if (this->include_internal_ || !obj->is_internal()) {
267  write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, EntityBase *obj) {
268  select::Select *select = (select::Select *) obj;
269  stream.print("<select>");
270  stream.print("<option></option>");
271  for (auto const &option : select->traits.get_options()) {
272  stream.print("<option>");
273  stream.print(option.c_str());
274  stream.print("</option>");
275  }
276  stream.print("</select>");
277  });
278  }
279  }
280 #endif
281 
282 #ifdef USE_LOCK
283  for (auto *obj : App.get_locks()) {
284  if (this->include_internal_ || !obj->is_internal()) {
285  write_row(stream, obj, "lock", "", [](AsyncResponseStream &stream, EntityBase *obj) {
286  lock::Lock *lock = (lock::Lock *) obj;
287  stream.print("<button>Lock</button><button>Unlock</button>");
288  if (lock->traits.get_supports_open()) {
289  stream.print("<button>Open</button>");
290  }
291  });
292  }
293  }
294 #endif
295 
296 #ifdef USE_CLIMATE
297  for (auto *obj : App.get_climates()) {
298  if (this->include_internal_ || !obj->is_internal())
299  write_row(stream, obj, "climate", "");
300  }
301 #endif
302 
303  stream->print(F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/index.html\">ESPHome Web API</a> for "
304  "REST API documentation.</p>"));
305  if (this->allow_ota_) {
306  stream->print(
307  F("<h2>OTA Update</h2><form method=\"POST\" action=\"/update\" enctype=\"multipart/form-data\"><input "
308  "type=\"file\" name=\"update\"><input type=\"submit\" value=\"Update\"></form>"));
309  }
310  stream->print(F("<h2>Debug Log</h2><pre id=\"log\"></pre>"));
311 #endif
312 #ifdef USE_WEBSERVER_JS_INCLUDE
313  if (this->js_include_ != nullptr) {
314  stream->print(F("<script type=\"module\" src=\"/0.js\"></script>"));
315  }
316 #endif
317 #if USE_WEBSERVER_VERSION == 2
318  stream->print(F("<esp-app></esp-app>"));
319 #endif
320  if (strlen(this->js_url_) > 0) {
321  stream->print(F("<script src=\""));
322  stream->print(this->js_url_);
323  stream->print(F("\"></script>"));
324  }
325 #if USE_WEBSERVER_VERSION == 1
326  stream->print(F("</article></body></html>"));
327 #else
328  stream->print(F("</body></html>"));
329 #endif
330 
331  request->send(stream);
332 }
333 #endif
334 #ifdef USE_WEBSERVER_CSS_INCLUDE
335 void WebServer::handle_css_request(AsyncWebServerRequest *request) {
336  AsyncResponseStream *stream = request->beginResponseStream("text/css");
337  if (this->css_include_ != nullptr) {
338  stream->print(this->css_include_);
339  }
340 
341  request->send(stream);
342 }
343 #endif
344 
345 #ifdef USE_WEBSERVER_JS_INCLUDE
346 void WebServer::handle_js_request(AsyncWebServerRequest *request) {
347  AsyncResponseStream *stream = request->beginResponseStream("text/javascript");
348  if (this->js_include_ != nullptr) {
349  stream->addHeader("Access-Control-Allow-Origin", "*");
350  stream->print(this->js_include_);
351  }
352 
353  request->send(stream);
354 }
355 #endif
356 
357 #define set_json_id(root, obj, sensor, start_config) \
358  (root)["id"] = sensor; \
359  if (((start_config) == DETAIL_ALL)) \
360  (root)["name"] = (obj)->get_name();
361 
362 #define set_json_value(root, obj, sensor, value, start_config) \
363  set_json_id((root), (obj), sensor, start_config)(root)["value"] = value;
364 
365 #define set_json_state_value(root, obj, sensor, state, value, start_config) \
366  set_json_value(root, obj, sensor, value, start_config)(root)["state"] = state;
367 
368 #define set_json_icon_state_value(root, obj, sensor, state, value, start_config) \
369  set_json_value(root, obj, sensor, value, start_config)(root)["state"] = state; \
370  if (((start_config) == DETAIL_ALL)) \
371  (root)["icon"] = (obj)->get_icon();
372 
373 #ifdef USE_SENSOR
375  this->events_.send(this->sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
376 }
377 void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
378  for (sensor::Sensor *obj : App.get_sensors()) {
379  if (obj->get_object_id() != match.id)
380  continue;
381  std::string data = this->sensor_json(obj, obj->state, DETAIL_STATE);
382  request->send(200, "application/json", data.c_str());
383  return;
384  }
385  request->send(404);
386 }
387 std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
388  return json::build_json([obj, value, start_config](JsonObject root) {
389  std::string state;
390  if (isnan(value)) {
391  state = "NA";
392  } else {
393  state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
394  if (!obj->get_unit_of_measurement().empty())
395  state += " " + obj->get_unit_of_measurement();
396  }
397  set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
398  });
399 }
400 #endif
401 
402 #ifdef USE_TEXT_SENSOR
404  this->events_.send(this->text_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
405 }
406 void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
408  if (obj->get_object_id() != match.id)
409  continue;
410  std::string data = this->text_sensor_json(obj, obj->state, DETAIL_STATE);
411  request->send(200, "application/json", data.c_str());
412  return;
413  }
414  request->send(404);
415 }
416 std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
417  JsonDetail start_config) {
418  return json::build_json([obj, value, start_config](JsonObject root) {
419  set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
420  });
421 }
422 #endif
423 
424 #ifdef USE_SWITCH
426  this->events_.send(this->switch_json(obj, state, DETAIL_STATE).c_str(), "state");
427 }
428 std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
429  return json::build_json([obj, value, start_config](JsonObject root) {
430  set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
431  if (start_config == DETAIL_ALL) {
432  root["assumed_state"] = obj->assumed_state();
433  }
434  });
435 }
436 void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
437  for (switch_::Switch *obj : App.get_switches()) {
438  if (obj->get_object_id() != match.id)
439  continue;
440 
441  if (request->method() == HTTP_GET) {
442  std::string data = this->switch_json(obj, obj->state, DETAIL_STATE);
443  request->send(200, "application/json", data.c_str());
444  } else if (match.method == "toggle") {
445  this->schedule_([obj]() { obj->toggle(); });
446  request->send(200);
447  } else if (match.method == "turn_on") {
448  this->schedule_([obj]() { obj->turn_on(); });
449  request->send(200);
450  } else if (match.method == "turn_off") {
451  this->schedule_([obj]() { obj->turn_off(); });
452  request->send(200);
453  } else {
454  request->send(404);
455  }
456  return;
457  }
458  request->send(404);
459 }
460 #endif
461 
462 #ifdef USE_BUTTON
463 std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
464  return json::build_json(
465  [obj, start_config](JsonObject root) { set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); });
466 }
467 
468 void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
469  for (button::Button *obj : App.get_buttons()) {
470  if (obj->get_object_id() != match.id)
471  continue;
472  if (request->method() == HTTP_POST && match.method == "press") {
473  this->schedule_([obj]() { obj->press(); });
474  request->send(200);
475  return;
476  } else {
477  request->send(404);
478  }
479  return;
480  }
481  request->send(404);
482 }
483 #endif
484 
485 #ifdef USE_BINARY_SENSOR
487  this->events_.send(this->binary_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
488 }
489 std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
490  return json::build_json([obj, value, start_config](JsonObject root) {
491  set_json_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
492  });
493 }
494 void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
496  if (obj->get_object_id() != match.id)
497  continue;
498  std::string data = this->binary_sensor_json(obj, obj->state, DETAIL_STATE);
499  request->send(200, "application/json", data.c_str());
500  return;
501  }
502  request->send(404);
503 }
504 #endif
505 
506 #ifdef USE_FAN
507 void WebServer::on_fan_update(fan::Fan *obj) { this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state"); }
508 std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
509  return json::build_json([obj, start_config](JsonObject root) {
510  set_json_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state, start_config);
511  const auto traits = obj->get_traits();
512  if (traits.supports_speed()) {
513  root["speed_level"] = obj->speed;
514  root["speed_count"] = traits.supported_speed_count();
515  }
516  if (obj->get_traits().supports_oscillation())
517  root["oscillation"] = obj->oscillating;
518  });
519 }
520 void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
521  for (fan::Fan *obj : App.get_fans()) {
522  if (obj->get_object_id() != match.id)
523  continue;
524 
525  if (request->method() == HTTP_GET) {
526  std::string data = this->fan_json(obj, DETAIL_STATE);
527  request->send(200, "application/json", data.c_str());
528  } else if (match.method == "toggle") {
529  this->schedule_([obj]() { obj->toggle().perform(); });
530  request->send(200);
531  } else if (match.method == "turn_on") {
532  auto call = obj->turn_on();
533  if (request->hasParam("speed")) {
534  String speed = request->getParam("speed")->value();
535  }
536  if (request->hasParam("speed_level")) {
537  String speed_level = request->getParam("speed_level")->value();
538  auto val = parse_number<int>(speed_level.c_str());
539  if (!val.has_value()) {
540  ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
541  return;
542  }
543  call.set_speed(*val);
544  }
545  if (request->hasParam("oscillation")) {
546  String speed = request->getParam("oscillation")->value();
547  auto val = parse_on_off(speed.c_str());
548  switch (val) {
549  case PARSE_ON:
550  call.set_oscillating(true);
551  break;
552  case PARSE_OFF:
553  call.set_oscillating(false);
554  break;
555  case PARSE_TOGGLE:
556  call.set_oscillating(!obj->oscillating);
557  break;
558  case PARSE_NONE:
559  request->send(404);
560  return;
561  }
562  }
563  this->schedule_([call]() mutable { call.perform(); });
564  request->send(200);
565  } else if (match.method == "turn_off") {
566  this->schedule_([obj]() { obj->turn_off().perform(); });
567  request->send(200);
568  } else {
569  request->send(404);
570  }
571  return;
572  }
573  request->send(404);
574 }
575 #endif
576 
577 #ifdef USE_LIGHT
579  this->events_.send(this->light_json(obj, DETAIL_STATE).c_str(), "state");
580 }
581 void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
582  for (light::LightState *obj : App.get_lights()) {
583  if (obj->get_object_id() != match.id)
584  continue;
585 
586  if (request->method() == HTTP_GET) {
587  std::string data = this->light_json(obj, DETAIL_STATE);
588  request->send(200, "application/json", data.c_str());
589  } else if (match.method == "toggle") {
590  this->schedule_([obj]() { obj->toggle().perform(); });
591  request->send(200);
592  } else if (match.method == "turn_on") {
593  auto call = obj->turn_on();
594  if (request->hasParam("brightness"))
595  call.set_brightness(request->getParam("brightness")->value().toFloat() / 255.0f);
596  if (request->hasParam("r"))
597  call.set_red(request->getParam("r")->value().toFloat() / 255.0f);
598  if (request->hasParam("g"))
599  call.set_green(request->getParam("g")->value().toFloat() / 255.0f);
600  if (request->hasParam("b"))
601  call.set_blue(request->getParam("b")->value().toFloat() / 255.0f);
602  if (request->hasParam("white_value"))
603  call.set_white(request->getParam("white_value")->value().toFloat() / 255.0f);
604  if (request->hasParam("color_temp"))
605  call.set_color_temperature(request->getParam("color_temp")->value().toFloat());
606 
607  if (request->hasParam("flash")) {
608  float length_s = request->getParam("flash")->value().toFloat();
609  call.set_flash_length(static_cast<uint32_t>(length_s * 1000));
610  }
611 
612  if (request->hasParam("transition")) {
613  float length_s = request->getParam("transition")->value().toFloat();
614  call.set_transition_length(static_cast<uint32_t>(length_s * 1000));
615  }
616 
617  if (request->hasParam("effect")) {
618  const char *effect = request->getParam("effect")->value().c_str();
619  call.set_effect(effect);
620  }
621 
622  this->schedule_([call]() mutable { call.perform(); });
623  request->send(200);
624  } else if (match.method == "turn_off") {
625  auto call = obj->turn_off();
626  if (request->hasParam("transition")) {
627  auto length = (uint32_t) request->getParam("transition")->value().toFloat() * 1000;
628  call.set_transition_length(length);
629  }
630  this->schedule_([call]() mutable { call.perform(); });
631  request->send(200);
632  } else {
633  request->send(404);
634  }
635  return;
636  }
637  request->send(404);
638 }
639 std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
640  return json::build_json([obj, start_config](JsonObject root) {
641  set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
642  root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
643 
645  if (start_config == DETAIL_ALL) {
646  JsonArray opt = root.createNestedArray("effects");
647  opt.add("None");
648  for (auto const &option : obj->get_effects()) {
649  opt.add(option->get_name());
650  }
651  }
652  });
653 }
654 #endif
655 
656 #ifdef USE_COVER
658  this->events_.send(this->cover_json(obj, DETAIL_STATE).c_str(), "state");
659 }
660 void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
661  for (cover::Cover *obj : App.get_covers()) {
662  if (obj->get_object_id() != match.id)
663  continue;
664 
665  if (request->method() == HTTP_GET) {
666  std::string data = this->cover_json(obj, DETAIL_STATE);
667  request->send(200, "application/json", data.c_str());
668  continue;
669  }
670 
671  auto call = obj->make_call();
672  if (match.method == "open") {
673  call.set_command_open();
674  } else if (match.method == "close") {
675  call.set_command_close();
676  } else if (match.method == "stop") {
677  call.set_command_stop();
678  } else if (match.method != "set") {
679  request->send(404);
680  return;
681  }
682 
683  auto traits = obj->get_traits();
684  if ((request->hasParam("position") && !traits.get_supports_position()) ||
685  (request->hasParam("tilt") && !traits.get_supports_tilt())) {
686  request->send(409);
687  return;
688  }
689 
690  if (request->hasParam("position"))
691  call.set_position(request->getParam("position")->value().toFloat());
692  if (request->hasParam("tilt"))
693  call.set_tilt(request->getParam("tilt")->value().toFloat());
694 
695  this->schedule_([call]() mutable { call.perform(); });
696  request->send(200);
697  return;
698  }
699  request->send(404);
700 }
701 std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
702  return json::build_json([obj, start_config](JsonObject root) {
703  set_json_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
704  obj->position, start_config);
705  root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
706 
707  if (obj->get_traits().get_supports_tilt())
708  root["tilt"] = obj->tilt;
709  });
710 }
711 #endif
712 
713 #ifdef USE_NUMBER
715  this->events_.send(this->number_json(obj, state, DETAIL_STATE).c_str(), "state");
716 }
717 void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
718  for (auto *obj : App.get_numbers()) {
719  if (obj->get_object_id() != match.id)
720  continue;
721 
722  if (request->method() == HTTP_GET) {
723  std::string data = this->number_json(obj, obj->state, DETAIL_STATE);
724  request->send(200, "application/json", data.c_str());
725  return;
726  }
727  if (match.method != "set") {
728  request->send(404);
729  return;
730  }
731 
732  auto call = obj->make_call();
733  if (request->hasParam("value")) {
734  String value = request->getParam("value")->value();
735  optional<float> value_f = parse_number<float>(value.c_str());
736  if (value_f.has_value())
737  call.set_value(*value_f);
738  }
739 
740  this->schedule_([call]() mutable { call.perform(); });
741  request->send(200);
742  return;
743  }
744  request->send(404);
745 }
746 
747 std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
748  return json::build_json([obj, value, start_config](JsonObject root) {
749  set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
750  if (start_config == DETAIL_ALL) {
751  root["min_value"] = obj->traits.get_min_value();
752  root["max_value"] = obj->traits.get_max_value();
753  root["step"] = obj->traits.get_step();
754  root["mode"] = (int) obj->traits.get_mode();
755  }
756  if (isnan(value)) {
757  root["value"] = "\"NaN\"";
758  root["state"] = "NA";
759  } else {
760  root["value"] = value;
762  if (!obj->traits.get_unit_of_measurement().empty())
763  state += " " + obj->traits.get_unit_of_measurement();
764  root["state"] = state;
765  }
766  });
767 }
768 #endif
769 
770 #ifdef USE_SELECT
771 void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
772  this->events_.send(this->select_json(obj, state, DETAIL_STATE).c_str(), "state");
773 }
774 void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
775  for (auto *obj : App.get_selects()) {
776  if (obj->get_object_id() != match.id)
777  continue;
778 
779  if (request->method() == HTTP_GET) {
780  std::string data = this->select_json(obj, obj->state, DETAIL_STATE);
781  request->send(200, "application/json", data.c_str());
782  return;
783  }
784 
785  if (match.method != "set") {
786  request->send(404);
787  return;
788  }
789 
790  auto call = obj->make_call();
791 
792  if (request->hasParam("option")) {
793  String option = request->getParam("option")->value();
794  call.set_option(option.c_str()); // NOLINT(clang-diagnostic-deprecated-declarations)
795  }
796 
797  this->schedule_([call]() mutable { call.perform(); });
798  request->send(200);
799  return;
800  }
801  request->send(404);
802 }
803 std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
804  return json::build_json([obj, value, start_config](JsonObject root) {
805  set_json_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
806  if (start_config == DETAIL_ALL) {
807  JsonArray opt = root.createNestedArray("option");
808  for (auto &option : obj->traits.get_options()) {
809  opt.add(option);
810  }
811  }
812  });
813 }
814 #endif
815 
816 #ifdef USE_CLIMATE
818  this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
819 }
820 
821 void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
822  for (auto *obj : App.get_climates()) {
823  if (obj->get_object_id() != match.id)
824  continue;
825 
826  if (request->method() == HTTP_GET) {
827  std::string data = this->climate_json(obj, DETAIL_STATE);
828  request->send(200, "application/json", data.c_str());
829  return;
830  }
831 
832  if (match.method != "set") {
833  request->send(404);
834  return;
835  }
836 
837  auto call = obj->make_call();
838 
839  if (request->hasParam("mode")) {
840  String mode = request->getParam("mode")->value();
841  call.set_mode(mode.c_str());
842  }
843 
844  if (request->hasParam("target_temperature_high")) {
845  String value = request->getParam("target_temperature_high")->value();
846  optional<float> value_f = parse_number<float>(value.c_str());
847  if (value_f.has_value())
848  call.set_target_temperature_high(*value_f);
849  }
850 
851  if (request->hasParam("target_temperature_low")) {
852  String value = request->getParam("target_temperature_low")->value();
853  optional<float> value_f = parse_number<float>(value.c_str());
854  if (value_f.has_value())
855  call.set_target_temperature_low(*value_f);
856  }
857 
858  if (request->hasParam("target_temperature")) {
859  String value = request->getParam("target_temperature")->value();
860  optional<float> value_f = parse_number<float>(value.c_str());
861  if (value_f.has_value())
862  call.set_target_temperature(*value_f);
863  }
864 
865  this->schedule_([call]() mutable { call.perform(); });
866  request->send(200);
867  return;
868  }
869  request->send(404);
870 }
871 
872 // Longest: HORIZONTAL
873 #define PSTR_LOCAL(mode_s) strncpy_P(__buf, (PGM_P) ((mode_s)), 15)
874 
875 std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
876  return json::build_json([obj, start_config](JsonObject root) {
877  set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
878  const auto traits = obj->get_traits();
879  int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
880  int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
881  char __buf[16];
882 
883  if (start_config == DETAIL_ALL) {
884  JsonArray opt = root.createNestedArray("modes");
885  for (climate::ClimateMode m : traits.get_supported_modes())
886  opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
887  if (!traits.get_supported_custom_fan_modes().empty()) {
888  JsonArray opt = root.createNestedArray("fan_modes");
889  for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
890  opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
891  }
892 
893  if (!traits.get_supported_custom_fan_modes().empty()) {
894  JsonArray opt = root.createNestedArray("custom_fan_modes");
895  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
896  opt.add(custom_fan_mode);
897  }
898  if (traits.get_supports_swing_modes()) {
899  JsonArray opt = root.createNestedArray("swing_modes");
900  for (auto swing_mode : traits.get_supported_swing_modes())
902  }
903  if (traits.get_supports_presets() && obj->preset.has_value()) {
904  JsonArray opt = root.createNestedArray("presets");
905  for (climate::ClimatePreset m : traits.get_supported_presets())
906  opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
907  }
908  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
909  JsonArray opt = root.createNestedArray("custom_presets");
910  for (auto const &custom_preset : traits.get_supported_custom_presets())
911  opt.add(custom_preset);
912  }
913  }
914 
915  bool has_state = false;
916  root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
917  root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
918  root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
919  root["step"] = traits.get_visual_target_temperature_step();
920  if (traits.get_supports_action()) {
921  root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
922  root["state"] = root["action"];
923  has_state = true;
924  }
925  if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
926  root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
927  }
928  if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
929  root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
930  }
931  if (traits.get_supports_presets() && obj->preset.has_value()) {
932  root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
933  }
934  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
935  root["custom_preset"] = obj->custom_preset.value().c_str();
936  }
937  if (traits.get_supports_swing_modes()) {
938  root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
939  }
940  if (traits.get_supports_current_temperature()) {
941  if (!std::isnan(obj->current_temperature)) {
942  root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
943  } else {
944  root["current_temperature"] = "NA";
945  }
946  }
947  if (traits.get_supports_two_point_target_temperature()) {
948  root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
949  root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
950  if (!has_state) {
951  root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
952  target_accuracy);
953  }
954  } else {
955  root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
956  if (!has_state)
957  root["state"] = root["target_temperature"];
958  }
959  });
960 }
961 #endif
962 
963 #ifdef USE_LOCK
965  this->events_.send(this->lock_json(obj, obj->state, DETAIL_STATE).c_str(), "state");
966 }
967 std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
968  return json::build_json([obj, value, start_config](JsonObject root) {
969  set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
970  start_config);
971  });
972 }
973 void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
974  for (lock::Lock *obj : App.get_locks()) {
975  if (obj->get_object_id() != match.id)
976  continue;
977 
978  if (request->method() == HTTP_GET) {
979  std::string data = this->lock_json(obj, obj->state, DETAIL_STATE);
980  request->send(200, "application/json", data.c_str());
981  } else if (match.method == "lock") {
982  this->schedule_([obj]() { obj->lock(); });
983  request->send(200);
984  } else if (match.method == "unlock") {
985  this->schedule_([obj]() { obj->unlock(); });
986  request->send(200);
987  } else if (match.method == "open") {
988  this->schedule_([obj]() { obj->open(); });
989  request->send(200);
990  } else {
991  request->send(404);
992  }
993  return;
994  }
995  request->send(404);
996 }
997 #endif
998 
999 bool WebServer::canHandle(AsyncWebServerRequest *request) {
1000  if (request->url() == "/")
1001  return true;
1002 
1003 #ifdef USE_WEBSERVER_CSS_INCLUDE
1004  if (request->url() == "/0.css")
1005  return true;
1006 #endif
1007 
1008 #ifdef USE_WEBSERVER_JS_INCLUDE
1009  if (request->url() == "/0.js")
1010  return true;
1011 #endif
1012 
1013  UrlMatch match = match_url(request->url().c_str(), true);
1014  if (!match.valid)
1015  return false;
1016 #ifdef USE_SENSOR
1017  if (request->method() == HTTP_GET && match.domain == "sensor")
1018  return true;
1019 #endif
1020 
1021 #ifdef USE_SWITCH
1022  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "switch")
1023  return true;
1024 #endif
1025 
1026 #ifdef USE_BUTTON
1027  if (request->method() == HTTP_POST && match.domain == "button")
1028  return true;
1029 #endif
1030 
1031 #ifdef USE_BINARY_SENSOR
1032  if (request->method() == HTTP_GET && match.domain == "binary_sensor")
1033  return true;
1034 #endif
1035 
1036 #ifdef USE_FAN
1037  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "fan")
1038  return true;
1039 #endif
1040 
1041 #ifdef USE_LIGHT
1042  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "light")
1043  return true;
1044 #endif
1045 
1046 #ifdef USE_TEXT_SENSOR
1047  if (request->method() == HTTP_GET && match.domain == "text_sensor")
1048  return true;
1049 #endif
1050 
1051 #ifdef USE_COVER
1052  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "cover")
1053  return true;
1054 #endif
1055 
1056 #ifdef USE_NUMBER
1057  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "number")
1058  return true;
1059 #endif
1060 
1061 #ifdef USE_SELECT
1062  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "select")
1063  return true;
1064 #endif
1065 
1066 #ifdef USE_CLIMATE
1067  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "climate")
1068  return true;
1069 #endif
1070 
1071 #ifdef USE_LOCK
1072  if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "lock")
1073  return true;
1074 #endif
1075 
1076  return false;
1077 }
1078 void WebServer::handleRequest(AsyncWebServerRequest *request) {
1079  if (request->url() == "/") {
1080  this->handle_index_request(request);
1081  return;
1082  }
1083 
1084 #ifdef USE_WEBSERVER_CSS_INCLUDE
1085  if (request->url() == "/0.css") {
1086  this->handle_css_request(request);
1087  return;
1088  }
1089 #endif
1090 
1091 #ifdef USE_WEBSERVER_JS_INCLUDE
1092  if (request->url() == "/0.js") {
1093  this->handle_js_request(request);
1094  return;
1095  }
1096 #endif
1097 
1098  UrlMatch match = match_url(request->url().c_str());
1099 #ifdef USE_SENSOR
1100  if (match.domain == "sensor") {
1101  this->handle_sensor_request(request, match);
1102  return;
1103  }
1104 #endif
1105 
1106 #ifdef USE_SWITCH
1107  if (match.domain == "switch") {
1108  this->handle_switch_request(request, match);
1109  return;
1110  }
1111 #endif
1112 
1113 #ifdef USE_BUTTON
1114  if (match.domain == "button") {
1115  this->handle_button_request(request, match);
1116  return;
1117  }
1118 #endif
1119 
1120 #ifdef USE_BINARY_SENSOR
1121  if (match.domain == "binary_sensor") {
1122  this->handle_binary_sensor_request(request, match);
1123  return;
1124  }
1125 #endif
1126 
1127 #ifdef USE_FAN
1128  if (match.domain == "fan") {
1129  this->handle_fan_request(request, match);
1130  return;
1131  }
1132 #endif
1133 
1134 #ifdef USE_LIGHT
1135  if (match.domain == "light") {
1136  this->handle_light_request(request, match);
1137  return;
1138  }
1139 #endif
1140 
1141 #ifdef USE_TEXT_SENSOR
1142  if (match.domain == "text_sensor") {
1143  this->handle_text_sensor_request(request, match);
1144  return;
1145  }
1146 #endif
1147 
1148 #ifdef USE_COVER
1149  if (match.domain == "cover") {
1150  this->handle_cover_request(request, match);
1151  return;
1152  }
1153 #endif
1154 
1155 #ifdef USE_NUMBER
1156  if (match.domain == "number") {
1157  this->handle_number_request(request, match);
1158  return;
1159  }
1160 #endif
1161 
1162 #ifdef USE_SELECT
1163  if (match.domain == "select") {
1164  this->handle_select_request(request, match);
1165  return;
1166  }
1167 #endif
1168 
1169 #ifdef USE_CLIMATE
1170  if (match.domain == "climate") {
1171  this->handle_climate_request(request, match);
1172  return;
1173  }
1174 #endif
1175 
1176 #ifdef USE_LOCK
1177  if (match.domain == "lock") {
1178  this->handle_lock_request(request, match);
1179 
1180  return;
1181  }
1182 #endif
1183 }
1184 
1185 bool WebServer::isRequestHandlerTrivial() { return false; }
1186 
1187 void WebServer::schedule_(std::function<void()> &&f) {
1188 #ifdef USE_ESP32
1189  xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);
1190  to_schedule_.push_back(std::move(f));
1191  xSemaphoreGive(this->to_schedule_lock_);
1192 #else
1193  this->defer(std::move(f));
1194 #endif
1195 }
1196 
1197 } // namespace web_server
1198 } // namespace esphome
1199 
1200 #endif // USE_ARDUINO
Base class for all switches.
Definition: switch.h:32
value_type const & value() const
Definition: optional.h:89
bool state
The current on/off state of the fan.
Definition: fan.h:103
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition: climate.h:185
void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a number request under &#39;/number/<id>&#39;.
Definition: web_server.cpp:717
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:105
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:51
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:747
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:387
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:273
void on_sensor_update(sensor::Sensor *obj, float state) override
Definition: web_server.cpp:374
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:314
WebServer(web_server_base::WebServerBase *base)
Definition: web_server.cpp:86
void handleRequest(AsyncWebServerRequest *request) override
Override the web handler&#39;s handleRequest method.
ClimatePreset
Enum for all preset modes.
Definition: climate_mode.h:82
const std::vector< climate::Climate * > & get_climates()
Definition: application.h:254
float target_temperature
The target temperature of the climate device.
Definition: climate.h:172
SemaphoreHandle_t to_schedule_lock_
Definition: web_server.h:240
std::string get_use_address()
Get the active network hostname.
Definition: util.cpp:44
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:494
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.
Definition: web_server.cpp:803
LockState state
The current reported state of the lock.
Definition: lock.h:122
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;.
Definition: web_server.cpp:774
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:124
ClimateMode mode
The active mode of the climate device.
Definition: climate.h:164
void on_lock_update(lock::Lock *obj) override
Definition: web_server.cpp:964
int speed
Definition: fan.h:35
virtual bool assumed_state()
Return whether this switch uses an assumed state - i.e.
Definition: switch.cpp:58
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:38
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
Definition: application.h:146
void setup() override
Setup the internal web server and register handlers.
Definition: web_server.cpp:98
SelectTraits traits
Definition: select.h:27
float target_temperature_high
The maximum target temperature of the climate device, for climate devices with split target temperatu...
Definition: climate.h:177
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition: climate.h:168
mopeka_std_values val[4]
const std::vector< fan::Fan * > & get_fans()
Definition: application.h:227
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override
Definition: web_server.cpp:486
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:581
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;.
Definition: web_server.cpp:973
bool has_value() const
Definition: optional.h:87
void handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match)
Handle a button request under &#39;/button/<id>/press&#39;.
Definition: web_server.cpp:468
bool supports_oscillation() const
Return if this fan supports oscillation.
Definition: fan_traits.h:13
void on_light_update(light::LightState *obj) override
Definition: web_server.cpp:578
const std::string & get_comment() const
Get the comment of this Application set by pre_setup().
Definition: application.h:148
float tilt
The current tilt value of the cover from 0.0 to 1.0.
Definition: cover.h:124
std::string get_object_id() const
Definition: entity_base.cpp:43
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:27
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:299
ClimateSwingMode swing_mode
Definition: climate.h:539
Internal helper struct that is used to parse incoming URLs.
Definition: web_server.h:21
LockTraits traits
Definition: lock.h:124
optional< std::string > custom_fan_mode
The active custom fan mode of the climate device.
Definition: climate.h:188
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:377
const std::vector< lock::Lock * > & get_locks()
Definition: application.h:281
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:416
std::string domain
The domain of the component, for example "sensor".
Definition: web_server.h:22
Logger * global_logger
Definition: logger.cpp:314
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:403
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:94
const std::vector< button::Button * > & get_buttons()
Definition: application.h:200
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:436
std::vector< std::string > get_options() const
optional< ClimatePreset > preset
The active preset of the climate device.
Definition: climate.h:191
uint8_t custom_preset
Definition: climate.h:537
UrlMatch match_url(const std::string &url, bool only_domain=false)
Definition: web_server.cpp:60
const std::vector< switch_::Switch * > & get_switches()
Definition: application.h:191
Base-class for all numbers.
Definition: number.h:39
const char * cover_operation_to_str(CoverOperation op)
Definition: cover.cpp:21
int speed
The current fan speed level.
Definition: fan.h:107
void handle_css_request(AsyncWebServerRequest *request)
Handle included css request under &#39;/0.css&#39;.
Definition: web_server.cpp:335
bool valid
Whether this match is valid.
Definition: web_server.h:25
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
Definition: web_server.cpp:771
ClimateTraits get_traits()
Get the traits of this climate device with all overrides applied.
Definition: climate.cpp:416
std::string get_unit_of_measurement()
Get the unit of measurement, using the manual override if set.
Definition: entity_base.cpp:87
const std::vector< text_sensor::TextSensor * > & get_text_sensors()
Definition: application.h:218
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:209
Application App
Global storage of Application pointer - only one Application can exist.
const std::vector< binary_sensor::BinarySensor * > & get_binary_sensors()
Definition: application.h:182
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:325
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:428
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:143
std::string light_json(light::LightState *obj, JsonDetail start_config)
Dump the light state as a JSON string.
Definition: web_server.cpp:639
static void dump_json(LightState &state, JsonObject root)
Dump the state of a light as JSON.
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:157
NumberTraits traits
Definition: number.h:49
void on_climate_update(climate::Climate *obj) override
Definition: web_server.cpp:817
const std::vector< cover::Cover * > & get_covers()
Definition: application.h:236
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:95
float get_setup_priority() const override
MQTT setup priority.
Definition: web_server.cpp:154
optional< std::string > custom_preset
The active custom preset mode of the climate device.
Definition: climate.h:194
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:182
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:520
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:93
std::string id
The id of the device that&#39;s being accessed, for example "living_room_fan".
Definition: web_server.h:23
const std::vector< light::LightState * > & get_lights()
Definition: application.h:245
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:463
void on_cover_update(cover::Cover *obj) override
Definition: web_server.cpp:657
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:660
void on_switch_update(switch_::Switch *obj, bool state) override
Definition: web_server.cpp:425
bool get_supports_tilt() const
Definition: cover_traits.h:14
void setup_controller(bool include_internal=false)
Definition: controller.cpp:7
Base-class for all selects.
Definition: select.h:24
void on_fan_update(fan::Fan *obj) override
Definition: web_server.cpp:507
web_server_base::WebServerBase * base_
Definition: web_server.h:229
Definition: a4988.cpp:4
void on_number_update(number::Number *obj, float state) override
Definition: web_server.cpp:714
std::string fan_json(fan::Fan *obj, JsonDetail start_config)
Dump the fan state as a JSON string.
Definition: web_server.cpp:508
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;.
Definition: web_server.cpp:821
int8_t get_accuracy_decimals()
Get the accuracy in decimals, using the manual override if set.
Definition: sensor.cpp:25
const std::vector< select::Select * > & get_selects()
Definition: application.h:272
Base-class for all sensors.
Definition: sensor.h:57
bool canHandle(AsyncWebServerRequest *request) override
Override the web handler&#39;s canHandle method.
Definition: web_server.cpp:999
ListEntitiesIterator entities_iterator_
Definition: web_server.h:231
std::deque< std::function< void()> > to_schedule_
Definition: web_server.h:239
const std::vector< number::Number * > & get_numbers()
Definition: application.h:263
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:406
uint8_t custom_fan_mode
Definition: climate.h:532
bool get_supports_open() const
Definition: lock.h:40
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.
Definition: web_server.cpp:967
float target_temperature_low
The minimum target temperature of the climate device, for climate devices with split target temperatu...
Definition: climate.h:175
const StringRef & get_name() const
Definition: entity_base.cpp:10
std::string climate_json(climate::Climate *obj, JsonDetail start_config)
Dump the climate details.
Definition: web_server.cpp:875
std::string method
The method that&#39;s being called, for example "turn_on".
Definition: web_server.h:24
Base class for all locks.
Definition: lock.h:103
ClimateAction action
The active state of the climate device.
Definition: climate.h:166
ClimateDevice - This is the base class for all climate integrations.
Definition: climate.h:161
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:489
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:701
void handle_js_request(AsyncWebServerRequest *request)
Handle included js request under &#39;/0.js&#39;.
Definition: web_server.cpp:346
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:96
const LogString * climate_swing_mode_to_string(ClimateSwingMode swing_mode)
Convert the given ClimateSwingMode to a human-readable string.