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