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