ESPHome 2025.6.3
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, "Running setup");
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,
286 "Web Server:\n"
287 " Address: %s:%u",
288 network::get_use_address().c_str(), this->base_->get_port());
289}
291
292#ifdef USE_WEBSERVER_LOCAL
293void WebServer::handle_index_request(AsyncWebServerRequest *request) {
294 AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
295 response->addHeader("Content-Encoding", "gzip");
296 request->send(response);
297}
298#elif USE_WEBSERVER_VERSION >= 2
299void WebServer::handle_index_request(AsyncWebServerRequest *request) {
300 AsyncWebServerResponse *response =
301 request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
302 // No gzip header here because the HTML file is so small
303 request->send(response);
304}
305#endif
306
307#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
308void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) {
309 AsyncWebServerResponse *response = request->beginResponse(200, "");
310 response->addHeader(HEADER_CORS_ALLOW_PNA, "true");
311 response->addHeader(HEADER_PNA_NAME, App.get_name().c_str());
312 std::string mac = get_mac_address_pretty();
313 response->addHeader(HEADER_PNA_ID, mac.c_str());
314 request->send(response);
315}
316#endif
317
318#ifdef USE_WEBSERVER_CSS_INCLUDE
319void WebServer::handle_css_request(AsyncWebServerRequest *request) {
320 AsyncWebServerResponse *response =
321 request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
322 response->addHeader("Content-Encoding", "gzip");
323 request->send(response);
324}
325#endif
326
327#ifdef USE_WEBSERVER_JS_INCLUDE
328void WebServer::handle_js_request(AsyncWebServerRequest *request) {
329 AsyncWebServerResponse *response =
330 request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
331 response->addHeader("Content-Encoding", "gzip");
332 request->send(response);
333}
334#endif
335
336#define set_json_id(root, obj, sensor, start_config) \
337 (root)["id"] = sensor; \
338 if (((start_config) == DETAIL_ALL)) { \
339 (root)["name"] = (obj)->get_name(); \
340 (root)["icon"] = (obj)->get_icon(); \
341 (root)["entity_category"] = (obj)->get_entity_category(); \
342 if ((obj)->is_disabled_by_default()) \
343 (root)["is_disabled_by_default"] = (obj)->is_disabled_by_default(); \
344 }
345
346#define set_json_value(root, obj, sensor, value, start_config) \
347 set_json_id((root), (obj), sensor, start_config); \
348 (root)["value"] = value;
349
350#define set_json_icon_state_value(root, obj, sensor, state, value, start_config) \
351 set_json_value(root, obj, sensor, value, start_config); \
352 (root)["state"] = state;
353
354#ifdef USE_SENSOR
356 if (this->events_.empty())
357 return;
359}
360void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
361 for (sensor::Sensor *obj : App.get_sensors()) {
362 if (obj->get_object_id() != match.id)
363 continue;
364 if (request->method() == HTTP_GET && match.method.empty()) {
365 auto detail = DETAIL_STATE;
366 auto *param = request->getParam("detail");
367 if (param && param->value() == "all") {
368 detail = DETAIL_ALL;
369 }
370 std::string data = this->sensor_json(obj, obj->state, detail);
371 request->send(200, "application/json", data.c_str());
372 return;
373 }
374 }
375 request->send(404);
376}
377std::string WebServer::sensor_state_json_generator(WebServer *web_server, void *source) {
378 return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_STATE);
379}
380std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *source) {
381 return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_ALL);
382}
383std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
384 return json::build_json([this, obj, value, start_config](JsonObject root) {
385 std::string state;
386 if (std::isnan(value)) {
387 state = "NA";
388 } else {
390 if (!obj->get_unit_of_measurement().empty())
391 state += " " + obj->get_unit_of_measurement();
392 }
393 set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
394 if (start_config == DETAIL_ALL) {
395 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
396 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
397 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
398 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
399 }
400 }
401 if (!obj->get_unit_of_measurement().empty())
402 root["uom"] = obj->get_unit_of_measurement();
403 }
404 });
405}
406#endif
407
408#ifdef USE_TEXT_SENSOR
410 if (this->events_.empty())
411 return;
413}
414void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
416 if (obj->get_object_id() != match.id)
417 continue;
418 if (request->method() == HTTP_GET && match.method.empty()) {
419 auto detail = DETAIL_STATE;
420 auto *param = request->getParam("detail");
421 if (param && param->value() == "all") {
422 detail = DETAIL_ALL;
423 }
424 std::string data = this->text_sensor_json(obj, obj->state, detail);
425 request->send(200, "application/json", data.c_str());
426 return;
427 }
428 }
429 request->send(404);
430}
431std::string WebServer::text_sensor_state_json_generator(WebServer *web_server, void *source) {
432 return web_server->text_sensor_json((text_sensor::TextSensor *) (source),
434}
435std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, void *source) {
436 return web_server->text_sensor_json((text_sensor::TextSensor *) (source),
437 ((text_sensor::TextSensor *) (source))->state, DETAIL_ALL);
438}
439std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
440 JsonDetail start_config) {
441 return json::build_json([this, obj, value, start_config](JsonObject root) {
442 set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
443 if (start_config == DETAIL_ALL) {
444 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
445 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
446 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
447 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
448 }
449 }
450 }
451 });
452}
453#endif
454
455#ifdef USE_SWITCH
457 if (this->events_.empty())
458 return;
460}
461void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
462 for (switch_::Switch *obj : App.get_switches()) {
463 if (obj->get_object_id() != match.id)
464 continue;
465
466 if (request->method() == HTTP_GET && match.method.empty()) {
467 auto detail = DETAIL_STATE;
468 auto *param = request->getParam("detail");
469 if (param && param->value() == "all") {
470 detail = DETAIL_ALL;
471 }
472 std::string data = this->switch_json(obj, obj->state, detail);
473 request->send(200, "application/json", data.c_str());
474 } else if (match.method == "toggle") {
475 this->schedule_([obj]() { obj->toggle(); });
476 request->send(200);
477 } else if (match.method == "turn_on") {
478 this->schedule_([obj]() { obj->turn_on(); });
479 request->send(200);
480 } else if (match.method == "turn_off") {
481 this->schedule_([obj]() { obj->turn_off(); });
482 request->send(200);
483 } else {
484 request->send(404);
485 }
486 return;
487 }
488 request->send(404);
489}
490std::string WebServer::switch_state_json_generator(WebServer *web_server, void *source) {
491 return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_STATE);
492}
493std::string WebServer::switch_all_json_generator(WebServer *web_server, void *source) {
494 return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_ALL);
495}
496std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
497 return json::build_json([this, obj, value, start_config](JsonObject root) {
498 set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
499 if (start_config == DETAIL_ALL) {
500 root["assumed_state"] = obj->assumed_state();
501 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
502 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
503 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
504 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
505 }
506 }
507 }
508 });
509}
510#endif
511
512#ifdef USE_BUTTON
513void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
514 for (button::Button *obj : App.get_buttons()) {
515 if (obj->get_object_id() != match.id)
516 continue;
517 if (request->method() == HTTP_GET && match.method.empty()) {
518 auto detail = DETAIL_STATE;
519 auto *param = request->getParam("detail");
520 if (param && param->value() == "all") {
521 detail = DETAIL_ALL;
522 }
523 std::string data = this->button_json(obj, detail);
524 request->send(200, "application/json", data.c_str());
525 } else if (match.method == "press") {
526 this->schedule_([obj]() { obj->press(); });
527 request->send(200);
528 return;
529 } else {
530 request->send(404);
531 }
532 return;
533 }
534 request->send(404);
535}
536std::string WebServer::button_state_json_generator(WebServer *web_server, void *source) {
537 return web_server->button_json((button::Button *) (source), DETAIL_STATE);
538}
539std::string WebServer::button_all_json_generator(WebServer *web_server, void *source) {
540 return web_server->button_json((button::Button *) (source), DETAIL_ALL);
541}
542std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
543 return json::build_json([this, obj, start_config](JsonObject root) {
544 set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
545 if (start_config == DETAIL_ALL) {
546 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
547 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
548 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
549 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
550 }
551 }
552 }
553 });
554}
555#endif
556
557#ifdef USE_BINARY_SENSOR
563void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
565 if (obj->get_object_id() != match.id)
566 continue;
567 if (request->method() == HTTP_GET && match.method.empty()) {
568 auto detail = DETAIL_STATE;
569 auto *param = request->getParam("detail");
570 if (param && param->value() == "all") {
571 detail = DETAIL_ALL;
572 }
573 std::string data = this->binary_sensor_json(obj, obj->state, detail);
574 request->send(200, "application/json", data.c_str());
575 return;
576 }
577 }
578 request->send(404);
579}
580std::string WebServer::binary_sensor_state_json_generator(WebServer *web_server, void *source) {
581 return web_server->binary_sensor_json((binary_sensor::BinarySensor *) (source),
583}
584std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, void *source) {
585 return web_server->binary_sensor_json((binary_sensor::BinarySensor *) (source),
587}
588std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
589 return json::build_json([this, obj, value, start_config](JsonObject root) {
590 set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
591 start_config);
592 if (start_config == DETAIL_ALL) {
593 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
594 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
595 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
596 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
597 }
598 }
599 }
600 });
601}
602#endif
603
604#ifdef USE_FAN
606 if (this->events_.empty())
607 return;
609}
610void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
611 for (fan::Fan *obj : App.get_fans()) {
612 if (obj->get_object_id() != match.id)
613 continue;
614
615 if (request->method() == HTTP_GET && match.method.empty()) {
616 auto detail = DETAIL_STATE;
617 auto *param = request->getParam("detail");
618 if (param && param->value() == "all") {
619 detail = DETAIL_ALL;
620 }
621 std::string data = this->fan_json(obj, detail);
622 request->send(200, "application/json", data.c_str());
623 } else if (match.method == "toggle") {
624 this->schedule_([obj]() { obj->toggle().perform(); });
625 request->send(200);
626 } else if (match.method == "turn_on" || match.method == "turn_off") {
627 auto call = match.method == "turn_on" ? obj->turn_on() : obj->turn_off();
628
629 if (request->hasParam("speed_level")) {
630 auto speed_level = request->getParam("speed_level")->value();
631 auto val = parse_number<int>(speed_level.c_str());
632 if (!val.has_value()) {
633 ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
634 return;
635 }
636 call.set_speed(*val);
637 }
638 if (request->hasParam("oscillation")) {
639 auto speed = request->getParam("oscillation")->value();
640 auto val = parse_on_off(speed.c_str());
641 switch (val) {
642 case PARSE_ON:
643 call.set_oscillating(true);
644 break;
645 case PARSE_OFF:
646 call.set_oscillating(false);
647 break;
648 case PARSE_TOGGLE:
649 call.set_oscillating(!obj->oscillating);
650 break;
651 case PARSE_NONE:
652 request->send(404);
653 return;
654 }
655 }
656 this->schedule_([call]() mutable { call.perform(); });
657 request->send(200);
658 } else {
659 request->send(404);
660 }
661 return;
662 }
663 request->send(404);
664}
665std::string WebServer::fan_state_json_generator(WebServer *web_server, void *source) {
666 return web_server->fan_json((fan::Fan *) (source), DETAIL_STATE);
667}
668std::string WebServer::fan_all_json_generator(WebServer *web_server, void *source) {
669 return web_server->fan_json((fan::Fan *) (source), DETAIL_ALL);
670}
671std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
672 return json::build_json([this, obj, start_config](JsonObject root) {
673 set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
674 start_config);
675 const auto traits = obj->get_traits();
676 if (traits.supports_speed()) {
677 root["speed_level"] = obj->speed;
678 root["speed_count"] = traits.supported_speed_count();
679 }
680 if (obj->get_traits().supports_oscillation())
681 root["oscillation"] = obj->oscillating;
682 if (start_config == DETAIL_ALL) {
683 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
684 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
685 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
686 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
687 }
688 }
689 }
690 });
691}
692#endif
693
694#ifdef USE_LIGHT
696 if (this->events_.empty())
697 return;
699}
700void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
701 for (light::LightState *obj : App.get_lights()) {
702 if (obj->get_object_id() != match.id)
703 continue;
704
705 if (request->method() == HTTP_GET && match.method.empty()) {
706 auto detail = DETAIL_STATE;
707 auto *param = request->getParam("detail");
708 if (param && param->value() == "all") {
709 detail = DETAIL_ALL;
710 }
711 std::string data = this->light_json(obj, detail);
712 request->send(200, "application/json", data.c_str());
713 } else if (match.method == "toggle") {
714 this->schedule_([obj]() { obj->toggle().perform(); });
715 request->send(200);
716 } else if (match.method == "turn_on") {
717 auto call = obj->turn_on();
718 if (request->hasParam("brightness")) {
719 auto brightness = parse_number<float>(request->getParam("brightness")->value().c_str());
720 if (brightness.has_value()) {
721 call.set_brightness(*brightness / 255.0f);
722 }
723 }
724 if (request->hasParam("r")) {
725 auto r = parse_number<float>(request->getParam("r")->value().c_str());
726 if (r.has_value()) {
727 call.set_red(*r / 255.0f);
728 }
729 }
730 if (request->hasParam("g")) {
731 auto g = parse_number<float>(request->getParam("g")->value().c_str());
732 if (g.has_value()) {
733 call.set_green(*g / 255.0f);
734 }
735 }
736 if (request->hasParam("b")) {
737 auto b = parse_number<float>(request->getParam("b")->value().c_str());
738 if (b.has_value()) {
739 call.set_blue(*b / 255.0f);
740 }
741 }
742 if (request->hasParam("white_value")) {
743 auto white_value = parse_number<float>(request->getParam("white_value")->value().c_str());
744 if (white_value.has_value()) {
745 call.set_white(*white_value / 255.0f);
746 }
747 }
748 if (request->hasParam("color_temp")) {
749 auto color_temp = parse_number<float>(request->getParam("color_temp")->value().c_str());
750 if (color_temp.has_value()) {
751 call.set_color_temperature(*color_temp);
752 }
753 }
754 if (request->hasParam("flash")) {
755 auto flash = parse_number<uint32_t>(request->getParam("flash")->value().c_str());
756 if (flash.has_value()) {
757 call.set_flash_length(*flash * 1000);
758 }
759 }
760 if (request->hasParam("transition")) {
761 auto transition = parse_number<uint32_t>(request->getParam("transition")->value().c_str());
762 if (transition.has_value()) {
763 call.set_transition_length(*transition * 1000);
764 }
765 }
766 if (request->hasParam("effect")) {
767 const char *effect = request->getParam("effect")->value().c_str();
768 call.set_effect(effect);
769 }
770
771 this->schedule_([call]() mutable { call.perform(); });
772 request->send(200);
773 } else if (match.method == "turn_off") {
774 auto call = obj->turn_off();
775 if (request->hasParam("transition")) {
776 auto transition = parse_number<uint32_t>(request->getParam("transition")->value().c_str());
777 if (transition.has_value()) {
778 call.set_transition_length(*transition * 1000);
779 }
780 }
781 this->schedule_([call]() mutable { call.perform(); });
782 request->send(200);
783 } else {
784 request->send(404);
785 }
786 return;
787 }
788 request->send(404);
789}
790std::string WebServer::light_state_json_generator(WebServer *web_server, void *source) {
791 return web_server->light_json((light::LightState *) (source), DETAIL_STATE);
792}
793std::string WebServer::light_all_json_generator(WebServer *web_server, void *source) {
794 return web_server->light_json((light::LightState *) (source), DETAIL_ALL);
795}
796std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
797 return json::build_json([this, obj, start_config](JsonObject root) {
798 set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
799 root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
800
802 if (start_config == DETAIL_ALL) {
803 JsonArray opt = root.createNestedArray("effects");
804 opt.add("None");
805 for (auto const &option : obj->get_effects()) {
806 opt.add(option->get_name());
807 }
808 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
809 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
810 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
811 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
812 }
813 }
814 }
815 });
816}
817#endif
818
819#ifdef USE_COVER
821 if (this->events_.empty())
822 return;
824}
825void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
826 for (cover::Cover *obj : App.get_covers()) {
827 if (obj->get_object_id() != match.id)
828 continue;
829
830 if (request->method() == HTTP_GET && match.method.empty()) {
831 auto detail = DETAIL_STATE;
832 auto *param = request->getParam("detail");
833 if (param && param->value() == "all") {
834 detail = DETAIL_ALL;
835 }
836 std::string data = this->cover_json(obj, detail);
837 request->send(200, "application/json", data.c_str());
838 return;
839 }
840
841 auto call = obj->make_call();
842 if (match.method == "open") {
843 call.set_command_open();
844 } else if (match.method == "close") {
845 call.set_command_close();
846 } else if (match.method == "stop") {
847 call.set_command_stop();
848 } else if (match.method == "toggle") {
849 call.set_command_toggle();
850 } else if (match.method != "set") {
851 request->send(404);
852 return;
853 }
854
855 auto traits = obj->get_traits();
856 if ((request->hasParam("position") && !traits.get_supports_position()) ||
857 (request->hasParam("tilt") && !traits.get_supports_tilt())) {
858 request->send(409);
859 return;
860 }
861
862 if (request->hasParam("position")) {
863 auto position = parse_number<float>(request->getParam("position")->value().c_str());
864 if (position.has_value()) {
865 call.set_position(*position);
866 }
867 }
868 if (request->hasParam("tilt")) {
869 auto tilt = parse_number<float>(request->getParam("tilt")->value().c_str());
870 if (tilt.has_value()) {
871 call.set_tilt(*tilt);
872 }
873 }
874
875 this->schedule_([call]() mutable { call.perform(); });
876 request->send(200);
877 return;
878 }
879 request->send(404);
880}
881std::string WebServer::cover_state_json_generator(WebServer *web_server, void *source) {
882 return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE);
883}
884std::string WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
885 return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE);
886}
887std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
888 return json::build_json([this, obj, start_config](JsonObject root) {
889 set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
890 obj->position, start_config);
891 root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
892
894 root["position"] = obj->position;
895 if (obj->get_traits().get_supports_tilt())
896 root["tilt"] = obj->tilt;
897 if (start_config == DETAIL_ALL) {
898 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
899 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
900 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
901 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
902 }
903 }
904 }
905 });
906}
907#endif
908
909#ifdef USE_NUMBER
911 if (this->events_.empty())
912 return;
914}
915void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
916 for (auto *obj : App.get_numbers()) {
917 if (obj->get_object_id() != match.id)
918 continue;
919
920 if (request->method() == HTTP_GET && match.method.empty()) {
921 auto detail = DETAIL_STATE;
922 auto *param = request->getParam("detail");
923 if (param && param->value() == "all") {
924 detail = DETAIL_ALL;
925 }
926 std::string data = this->number_json(obj, obj->state, detail);
927 request->send(200, "application/json", data.c_str());
928 return;
929 }
930 if (match.method != "set") {
931 request->send(404);
932 return;
933 }
934
935 auto call = obj->make_call();
936 if (request->hasParam("value")) {
937 auto value = parse_number<float>(request->getParam("value")->value().c_str());
938 if (value.has_value())
939 call.set_value(*value);
940 }
941
942 this->schedule_([call]() mutable { call.perform(); });
943 request->send(200);
944 return;
945 }
946 request->send(404);
947}
948
949std::string WebServer::number_state_json_generator(WebServer *web_server, void *source) {
950 return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_STATE);
951}
952std::string WebServer::number_all_json_generator(WebServer *web_server, void *source) {
953 return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_ALL);
954}
955std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
956 return json::build_json([this, obj, value, start_config](JsonObject root) {
957 set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
958 if (start_config == DETAIL_ALL) {
959 root["min_value"] =
961 root["max_value"] =
963 root["step"] =
965 root["mode"] = (int) obj->traits.get_mode();
966 if (!obj->traits.get_unit_of_measurement().empty())
967 root["uom"] = obj->traits.get_unit_of_measurement();
968 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
969 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
970 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
971 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
972 }
973 }
974 }
975 if (std::isnan(value)) {
976 root["value"] = "\"NaN\"";
977 root["state"] = "NA";
978 } else {
981 if (!obj->traits.get_unit_of_measurement().empty())
982 state += " " + obj->traits.get_unit_of_measurement();
983 root["state"] = state;
984 }
985 });
986}
987#endif
988
989#ifdef USE_DATETIME_DATE
991 if (this->events_.empty())
992 return;
994}
995void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
996 for (auto *obj : App.get_dates()) {
997 if (obj->get_object_id() != match.id)
998 continue;
999 if (request->method() == HTTP_GET && match.method.empty()) {
1000 auto detail = DETAIL_STATE;
1001 auto *param = request->getParam("detail");
1002 if (param && param->value() == "all") {
1003 detail = DETAIL_ALL;
1004 }
1005 std::string data = this->date_json(obj, detail);
1006 request->send(200, "application/json", data.c_str());
1007 return;
1008 }
1009 if (match.method != "set") {
1010 request->send(404);
1011 return;
1012 }
1013
1014 auto call = obj->make_call();
1015
1016 if (!request->hasParam("value")) {
1017 request->send(409);
1018 return;
1019 }
1020
1021 if (request->hasParam("value")) {
1022 std::string value = request->getParam("value")->value().c_str(); // NOLINT
1023 call.set_date(value);
1024 }
1025
1026 this->schedule_([call]() mutable { call.perform(); });
1027 request->send(200);
1028 return;
1029 }
1030 request->send(404);
1031}
1032
1033std::string WebServer::date_state_json_generator(WebServer *web_server, void *source) {
1034 return web_server->date_json((datetime::DateEntity *) (source), DETAIL_STATE);
1035}
1036std::string WebServer::date_all_json_generator(WebServer *web_server, void *source) {
1037 return web_server->date_json((datetime::DateEntity *) (source), DETAIL_ALL);
1038}
1040 return json::build_json([this, obj, start_config](JsonObject root) {
1041 set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
1042 std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
1043 root["value"] = value;
1044 root["state"] = value;
1045 if (start_config == DETAIL_ALL) {
1046 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1047 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1048 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1049 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1050 }
1051 }
1052 }
1053 });
1054}
1055#endif // USE_DATETIME_DATE
1056
1057#ifdef USE_DATETIME_TIME
1059 if (this->events_.empty())
1060 return;
1062}
1063void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1064 for (auto *obj : App.get_times()) {
1065 if (obj->get_object_id() != match.id)
1066 continue;
1067 if (request->method() == HTTP_GET && match.method.empty()) {
1068 auto detail = DETAIL_STATE;
1069 auto *param = request->getParam("detail");
1070 if (param && param->value() == "all") {
1071 detail = DETAIL_ALL;
1072 }
1073 std::string data = this->time_json(obj, detail);
1074 request->send(200, "application/json", data.c_str());
1075 return;
1076 }
1077 if (match.method != "set") {
1078 request->send(404);
1079 return;
1080 }
1081
1082 auto call = obj->make_call();
1083
1084 if (!request->hasParam("value")) {
1085 request->send(409);
1086 return;
1087 }
1088
1089 if (request->hasParam("value")) {
1090 std::string value = request->getParam("value")->value().c_str(); // NOLINT
1091 call.set_time(value);
1092 }
1093
1094 this->schedule_([call]() mutable { call.perform(); });
1095 request->send(200);
1096 return;
1097 }
1098 request->send(404);
1099}
1100std::string WebServer::time_state_json_generator(WebServer *web_server, void *source) {
1101 return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_STATE);
1102}
1103std::string WebServer::time_all_json_generator(WebServer *web_server, void *source) {
1104 return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_ALL);
1105}
1107 return json::build_json([this, obj, start_config](JsonObject root) {
1108 set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
1109 std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
1110 root["value"] = value;
1111 root["state"] = value;
1112 if (start_config == DETAIL_ALL) {
1113 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1114 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1115 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1116 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1117 }
1118 }
1119 }
1120 });
1121}
1122#endif // USE_DATETIME_TIME
1123
1124#ifdef USE_DATETIME_DATETIME
1126 if (this->events_.empty())
1127 return;
1129}
1130void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1131 for (auto *obj : App.get_datetimes()) {
1132 if (obj->get_object_id() != match.id)
1133 continue;
1134 if (request->method() == HTTP_GET && match.method.empty()) {
1135 auto detail = DETAIL_STATE;
1136 auto *param = request->getParam("detail");
1137 if (param && param->value() == "all") {
1138 detail = DETAIL_ALL;
1139 }
1140 std::string data = this->datetime_json(obj, detail);
1141 request->send(200, "application/json", data.c_str());
1142 return;
1143 }
1144 if (match.method != "set") {
1145 request->send(404);
1146 return;
1147 }
1148
1149 auto call = obj->make_call();
1150
1151 if (!request->hasParam("value")) {
1152 request->send(409);
1153 return;
1154 }
1155
1156 if (request->hasParam("value")) {
1157 std::string value = request->getParam("value")->value().c_str(); // NOLINT
1158 call.set_datetime(value);
1159 }
1160
1161 this->schedule_([call]() mutable { call.perform(); });
1162 request->send(200);
1163 return;
1164 }
1165 request->send(404);
1166}
1167std::string WebServer::datetime_state_json_generator(WebServer *web_server, void *source) {
1168 return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_STATE);
1169}
1170std::string WebServer::datetime_all_json_generator(WebServer *web_server, void *source) {
1171 return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_ALL);
1172}
1174 return json::build_json([this, obj, start_config](JsonObject root) {
1175 set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
1176 std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour,
1177 obj->minute, obj->second);
1178 root["value"] = value;
1179 root["state"] = value;
1180 if (start_config == DETAIL_ALL) {
1181 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1182 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1183 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1184 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1185 }
1186 }
1187 }
1188 });
1189}
1190#endif // USE_DATETIME_DATETIME
1191
1192#ifdef USE_TEXT
1193void WebServer::on_text_update(text::Text *obj, const std::string &state) {
1194 if (this->events_.empty())
1195 return;
1197}
1198void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1199 for (auto *obj : App.get_texts()) {
1200 if (obj->get_object_id() != match.id)
1201 continue;
1202
1203 if (request->method() == HTTP_GET && match.method.empty()) {
1204 auto detail = DETAIL_STATE;
1205 auto *param = request->getParam("detail");
1206 if (param && param->value() == "all") {
1207 detail = DETAIL_ALL;
1208 }
1209 std::string data = this->text_json(obj, obj->state, detail);
1210 request->send(200, "application/json", data.c_str());
1211 return;
1212 }
1213 if (match.method != "set") {
1214 request->send(404);
1215 return;
1216 }
1217
1218 auto call = obj->make_call();
1219 if (request->hasParam("value")) {
1220 String value = request->getParam("value")->value();
1221 call.set_value(value.c_str()); // NOLINT
1222 }
1223
1224 this->defer([call]() mutable { call.perform(); });
1225 request->send(200);
1226 return;
1227 }
1228 request->send(404);
1229}
1230
1231std::string WebServer::text_state_json_generator(WebServer *web_server, void *source) {
1232 return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_STATE);
1233}
1234std::string WebServer::text_all_json_generator(WebServer *web_server, void *source) {
1235 return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_ALL);
1236}
1237std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
1238 return json::build_json([this, obj, value, start_config](JsonObject root) {
1239 set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
1240 root["min_length"] = obj->traits.get_min_length();
1241 root["max_length"] = obj->traits.get_max_length();
1242 root["pattern"] = obj->traits.get_pattern();
1244 root["state"] = "********";
1245 } else {
1246 root["state"] = value;
1247 }
1248 root["value"] = value;
1249 if (start_config == DETAIL_ALL) {
1250 root["mode"] = (int) obj->traits.get_mode();
1251 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1252 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1253 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1254 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1255 }
1256 }
1257 }
1258 });
1259}
1260#endif
1261
1262#ifdef USE_SELECT
1263void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
1264 if (this->events_.empty())
1265 return;
1267}
1268void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1269 for (auto *obj : App.get_selects()) {
1270 if (obj->get_object_id() != match.id)
1271 continue;
1272
1273 if (request->method() == HTTP_GET && match.method.empty()) {
1274 auto detail = DETAIL_STATE;
1275 auto *param = request->getParam("detail");
1276 if (param && param->value() == "all") {
1277 detail = DETAIL_ALL;
1278 }
1279 std::string data = this->select_json(obj, obj->state, detail);
1280 request->send(200, "application/json", data.c_str());
1281 return;
1282 }
1283
1284 if (match.method != "set") {
1285 request->send(404);
1286 return;
1287 }
1288
1289 auto call = obj->make_call();
1290
1291 if (request->hasParam("option")) {
1292 auto option = request->getParam("option")->value();
1293 call.set_option(option.c_str()); // NOLINT
1294 }
1295
1296 this->schedule_([call]() mutable { call.perform(); });
1297 request->send(200);
1298 return;
1299 }
1300 request->send(404);
1301}
1302std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) {
1303 return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_STATE);
1304}
1305std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) {
1306 return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL);
1307}
1308std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
1309 return json::build_json([this, obj, value, start_config](JsonObject root) {
1310 set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
1311 if (start_config == DETAIL_ALL) {
1312 JsonArray opt = root.createNestedArray("option");
1313 for (auto &option : obj->traits.get_options()) {
1314 opt.add(option);
1315 }
1316 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1317 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1318 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1319 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1320 }
1321 }
1322 }
1323 });
1324}
1325#endif
1326
1327// Longest: HORIZONTAL
1328#define PSTR_LOCAL(mode_s) strncpy_P(buf, (PGM_P) ((mode_s)), 15)
1329
1330#ifdef USE_CLIMATE
1332 if (this->events_.empty())
1333 return;
1335}
1336void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1337 for (auto *obj : App.get_climates()) {
1338 if (obj->get_object_id() != match.id)
1339 continue;
1340
1341 if (request->method() == HTTP_GET && match.method.empty()) {
1342 auto detail = DETAIL_STATE;
1343 auto *param = request->getParam("detail");
1344 if (param && param->value() == "all") {
1345 detail = DETAIL_ALL;
1346 }
1347 std::string data = this->climate_json(obj, detail);
1348 request->send(200, "application/json", data.c_str());
1349 return;
1350 }
1351
1352 if (match.method != "set") {
1353 request->send(404);
1354 return;
1355 }
1356
1357 auto call = obj->make_call();
1358
1359 if (request->hasParam("mode")) {
1360 auto mode = request->getParam("mode")->value();
1361 call.set_mode(mode.c_str()); // NOLINT
1362 }
1363
1364 if (request->hasParam("fan_mode")) {
1365 auto mode = request->getParam("fan_mode")->value();
1366 call.set_fan_mode(mode.c_str()); // NOLINT
1367 }
1368
1369 if (request->hasParam("swing_mode")) {
1370 auto mode = request->getParam("swing_mode")->value();
1371 call.set_swing_mode(mode.c_str()); // NOLINT
1372 }
1373
1374 if (request->hasParam("target_temperature_high")) {
1375 auto target_temperature_high = parse_number<float>(request->getParam("target_temperature_high")->value().c_str());
1376 if (target_temperature_high.has_value())
1377 call.set_target_temperature_high(*target_temperature_high);
1378 }
1379
1380 if (request->hasParam("target_temperature_low")) {
1381 auto target_temperature_low = parse_number<float>(request->getParam("target_temperature_low")->value().c_str());
1382 if (target_temperature_low.has_value())
1383 call.set_target_temperature_low(*target_temperature_low);
1384 }
1385
1386 if (request->hasParam("target_temperature")) {
1387 auto target_temperature = parse_number<float>(request->getParam("target_temperature")->value().c_str());
1388 if (target_temperature.has_value())
1389 call.set_target_temperature(*target_temperature);
1390 }
1391
1392 this->schedule_([call]() mutable { call.perform(); });
1393 request->send(200);
1394 return;
1395 }
1396 request->send(404);
1397}
1398std::string WebServer::climate_state_json_generator(WebServer *web_server, void *source) {
1399 return web_server->climate_json((climate::Climate *) (source), DETAIL_STATE);
1400}
1401std::string WebServer::climate_all_json_generator(WebServer *web_server, void *source) {
1402 return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
1403}
1404std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
1405 return json::build_json([this, obj, start_config](JsonObject root) {
1406 set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
1407 const auto traits = obj->get_traits();
1408 int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
1409 int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
1410 char buf[16];
1411
1412 if (start_config == DETAIL_ALL) {
1413 JsonArray opt = root.createNestedArray("modes");
1414 for (climate::ClimateMode m : traits.get_supported_modes())
1415 opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
1416 if (!traits.get_supported_custom_fan_modes().empty()) {
1417 JsonArray opt = root.createNestedArray("fan_modes");
1418 for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
1419 opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
1420 }
1421
1422 if (!traits.get_supported_custom_fan_modes().empty()) {
1423 JsonArray opt = root.createNestedArray("custom_fan_modes");
1424 for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
1425 opt.add(custom_fan_mode);
1426 }
1427 if (traits.get_supports_swing_modes()) {
1428 JsonArray opt = root.createNestedArray("swing_modes");
1429 for (auto swing_mode : traits.get_supported_swing_modes())
1431 }
1432 if (traits.get_supports_presets() && obj->preset.has_value()) {
1433 JsonArray opt = root.createNestedArray("presets");
1434 for (climate::ClimatePreset m : traits.get_supported_presets())
1435 opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
1436 }
1437 if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1438 JsonArray opt = root.createNestedArray("custom_presets");
1439 for (auto const &custom_preset : traits.get_supported_custom_presets())
1440 opt.add(custom_preset);
1441 }
1442 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1443 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1444 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1445 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1446 }
1447 }
1448 }
1449
1450 bool has_state = false;
1451 root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
1452 root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
1453 root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
1454 root["step"] = traits.get_visual_target_temperature_step();
1455 if (traits.get_supports_action()) {
1456 root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
1457 root["state"] = root["action"];
1458 has_state = true;
1459 }
1460 if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
1461 root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
1462 }
1463 if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
1464 root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
1465 }
1466 if (traits.get_supports_presets() && obj->preset.has_value()) {
1467 root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
1468 }
1469 if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1470 root["custom_preset"] = obj->custom_preset.value().c_str();
1471 }
1472 if (traits.get_supports_swing_modes()) {
1473 root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
1474 }
1475 if (traits.get_supports_current_temperature()) {
1476 if (!std::isnan(obj->current_temperature)) {
1477 root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
1478 } else {
1479 root["current_temperature"] = "NA";
1480 }
1481 }
1482 if (traits.get_supports_two_point_target_temperature()) {
1483 root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
1484 root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
1485 if (!has_state) {
1486 root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
1487 target_accuracy);
1488 }
1489 } else {
1490 root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
1491 if (!has_state)
1492 root["state"] = root["target_temperature"];
1493 }
1494 });
1495}
1496#endif
1497
1498#ifdef USE_LOCK
1500 if (this->events_.empty())
1501 return;
1503}
1504void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1505 for (lock::Lock *obj : App.get_locks()) {
1506 if (obj->get_object_id() != match.id)
1507 continue;
1508
1509 if (request->method() == HTTP_GET && match.method.empty()) {
1510 auto detail = DETAIL_STATE;
1511 auto *param = request->getParam("detail");
1512 if (param && param->value() == "all") {
1513 detail = DETAIL_ALL;
1514 }
1515 std::string data = this->lock_json(obj, obj->state, detail);
1516 request->send(200, "application/json", data.c_str());
1517 } else if (match.method == "lock") {
1518 this->schedule_([obj]() { obj->lock(); });
1519 request->send(200);
1520 } else if (match.method == "unlock") {
1521 this->schedule_([obj]() { obj->unlock(); });
1522 request->send(200);
1523 } else if (match.method == "open") {
1524 this->schedule_([obj]() { obj->open(); });
1525 request->send(200);
1526 } else {
1527 request->send(404);
1528 }
1529 return;
1530 }
1531 request->send(404);
1532}
1533std::string WebServer::lock_state_json_generator(WebServer *web_server, void *source) {
1534 return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_STATE);
1535}
1536std::string WebServer::lock_all_json_generator(WebServer *web_server, void *source) {
1537 return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_ALL);
1538}
1539std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
1540 return json::build_json([this, obj, value, start_config](JsonObject root) {
1541 set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
1542 start_config);
1543 if (start_config == DETAIL_ALL) {
1544 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1545 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1546 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1547 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1548 }
1549 }
1550 }
1551 });
1552}
1553#endif
1554
1555#ifdef USE_VALVE
1557 if (this->events_.empty())
1558 return;
1560}
1561void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1562 for (valve::Valve *obj : App.get_valves()) {
1563 if (obj->get_object_id() != match.id)
1564 continue;
1565
1566 if (request->method() == HTTP_GET && match.method.empty()) {
1567 auto detail = DETAIL_STATE;
1568 auto *param = request->getParam("detail");
1569 if (param && param->value() == "all") {
1570 detail = DETAIL_ALL;
1571 }
1572 std::string data = this->valve_json(obj, detail);
1573 request->send(200, "application/json", data.c_str());
1574 return;
1575 }
1576
1577 auto call = obj->make_call();
1578 if (match.method == "open") {
1579 call.set_command_open();
1580 } else if (match.method == "close") {
1581 call.set_command_close();
1582 } else if (match.method == "stop") {
1583 call.set_command_stop();
1584 } else if (match.method == "toggle") {
1585 call.set_command_toggle();
1586 } else if (match.method != "set") {
1587 request->send(404);
1588 return;
1589 }
1590
1591 auto traits = obj->get_traits();
1592 if (request->hasParam("position") && !traits.get_supports_position()) {
1593 request->send(409);
1594 return;
1595 }
1596
1597 if (request->hasParam("position")) {
1598 auto position = parse_number<float>(request->getParam("position")->value().c_str());
1599 if (position.has_value()) {
1600 call.set_position(*position);
1601 }
1602 }
1603
1604 this->schedule_([call]() mutable { call.perform(); });
1605 request->send(200);
1606 return;
1607 }
1608 request->send(404);
1609}
1610std::string WebServer::valve_state_json_generator(WebServer *web_server, void *source) {
1611 return web_server->valve_json((valve::Valve *) (source), DETAIL_STATE);
1612}
1613std::string WebServer::valve_all_json_generator(WebServer *web_server, void *source) {
1614 return web_server->valve_json((valve::Valve *) (source), DETAIL_ALL);
1615}
1616std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
1617 return json::build_json([this, obj, start_config](JsonObject root) {
1618 set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
1619 obj->position, start_config);
1620 root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
1621
1622 if (obj->get_traits().get_supports_position())
1623 root["position"] = obj->position;
1624 if (start_config == DETAIL_ALL) {
1625 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1626 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1627 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1628 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1629 }
1630 }
1631 }
1632 });
1633}
1634#endif
1635
1636#ifdef USE_ALARM_CONTROL_PANEL
1642void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1644 if (obj->get_object_id() != match.id)
1645 continue;
1646
1647 if (request->method() == HTTP_GET && match.method.empty()) {
1648 auto detail = DETAIL_STATE;
1649 auto *param = request->getParam("detail");
1650 if (param && param->value() == "all") {
1651 detail = DETAIL_ALL;
1652 }
1653 std::string data = this->alarm_control_panel_json(obj, obj->get_state(), detail);
1654 request->send(200, "application/json", data.c_str());
1655 return;
1656 }
1657
1658 auto call = obj->make_call();
1659 if (request->hasParam("code")) {
1660 call.set_code(request->getParam("code")->value().c_str()); // NOLINT
1661 }
1662
1663 if (match.method == "disarm") {
1664 call.disarm();
1665 } else if (match.method == "arm_away") {
1666 call.arm_away();
1667 } else if (match.method == "arm_home") {
1668 call.arm_home();
1669 } else if (match.method == "arm_night") {
1670 call.arm_night();
1671 } else if (match.method == "arm_vacation") {
1672 call.arm_vacation();
1673 } else {
1674 request->send(404);
1675 return;
1676 }
1677
1678 this->schedule_([call]() mutable { call.perform(); });
1679 request->send(200);
1680 return;
1681 }
1682 request->send(404);
1683}
1686 ((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
1687 DETAIL_STATE);
1688}
1689std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_server, void *source) {
1691 ((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
1692 DETAIL_ALL);
1693}
1696 JsonDetail start_config) {
1697 return json::build_json([this, obj, value, start_config](JsonObject root) {
1698 char buf[16];
1699 set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
1700 PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
1701 if (start_config == DETAIL_ALL) {
1702 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1703 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1704 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1705 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1706 }
1707 }
1708 }
1709 });
1710}
1711#endif
1712
1713#ifdef USE_EVENT
1714void WebServer::on_event(event::Event *obj, const std::string &event_type) {
1716}
1717
1718void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1719 for (event::Event *obj : App.get_events()) {
1720 if (obj->get_object_id() != match.id)
1721 continue;
1722
1723 if (request->method() == HTTP_GET && match.method.empty()) {
1724 auto detail = DETAIL_STATE;
1725 auto *param = request->getParam("detail");
1726 if (param && param->value() == "all") {
1727 detail = DETAIL_ALL;
1728 }
1729 std::string data = this->event_json(obj, "", detail);
1730 request->send(200, "application/json", data.c_str());
1731 return;
1732 }
1733 }
1734 request->send(404);
1735}
1736
1737static std::string get_event_type(event::Event *event) { return event->last_event_type ? *event->last_event_type : ""; }
1738
1739std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) {
1740 auto *event = static_cast<event::Event *>(source);
1741 return web_server->event_json(event, get_event_type(event), DETAIL_STATE);
1742}
1743std::string WebServer::event_all_json_generator(WebServer *web_server, void *source) {
1744 auto *event = static_cast<event::Event *>(source);
1745 return web_server->event_json(event, get_event_type(event), DETAIL_ALL);
1746}
1747std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
1748 return json::build_json([this, obj, event_type, start_config](JsonObject root) {
1749 set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
1750 if (!event_type.empty()) {
1751 root["event_type"] = event_type;
1752 }
1753 if (start_config == DETAIL_ALL) {
1754 JsonArray event_types = root.createNestedArray("event_types");
1755 for (auto const &event_type : obj->get_event_types()) {
1756 event_types.add(event_type);
1757 }
1758 root["device_class"] = obj->get_device_class();
1759 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1760 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1761 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1762 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1763 }
1764 }
1765 }
1766 });
1767}
1768#endif
1769
1770#ifdef USE_UPDATE
1772 if (this->events_.empty())
1773 return;
1775}
1776void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1777 for (update::UpdateEntity *obj : App.get_updates()) {
1778 if (obj->get_object_id() != match.id)
1779 continue;
1780
1781 if (request->method() == HTTP_GET && match.method.empty()) {
1782 auto detail = DETAIL_STATE;
1783 auto *param = request->getParam("detail");
1784 if (param && param->value() == "all") {
1785 detail = DETAIL_ALL;
1786 }
1787 std::string data = this->update_json(obj, detail);
1788 request->send(200, "application/json", data.c_str());
1789 return;
1790 }
1791
1792 if (match.method != "install") {
1793 request->send(404);
1794 return;
1795 }
1796
1797 this->schedule_([obj]() mutable { obj->perform(); });
1798 request->send(200);
1799 return;
1800 }
1801 request->send(404);
1802}
1803std::string WebServer::update_state_json_generator(WebServer *web_server, void *source) {
1804 return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1805}
1806std::string WebServer::update_all_json_generator(WebServer *web_server, void *source) {
1807 return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1808}
1810 return json::build_json([this, obj, start_config](JsonObject root) {
1811 set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
1812 root["value"] = obj->update_info.latest_version;
1813 switch (obj->state) {
1815 root["state"] = "NO UPDATE";
1816 break;
1818 root["state"] = "UPDATE AVAILABLE";
1819 break;
1821 root["state"] = "INSTALLING";
1822 break;
1823 default:
1824 root["state"] = "UNKNOWN";
1825 break;
1826 }
1827 if (start_config == DETAIL_ALL) {
1828 root["current_version"] = obj->update_info.current_version;
1829 root["title"] = obj->update_info.title;
1830 root["summary"] = obj->update_info.summary;
1831 root["release_url"] = obj->update_info.release_url;
1832 if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1833 root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1834 if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1835 root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1836 }
1837 }
1838 }
1839 });
1840}
1841#endif
1842
1843bool WebServer::canHandle(AsyncWebServerRequest *request) {
1844 if (request->url() == "/")
1845 return true;
1846
1847#ifdef USE_ARDUINO
1848 if (request->url() == "/events") {
1849 return true;
1850 }
1851#endif
1852
1853#ifdef USE_WEBSERVER_CSS_INCLUDE
1854 if (request->url() == "/0.css")
1855 return true;
1856#endif
1857
1858#ifdef USE_WEBSERVER_JS_INCLUDE
1859 if (request->url() == "/0.js")
1860 return true;
1861#endif
1862
1863#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
1864 if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
1865#ifdef USE_ARDUINO
1866 // Header needs to be added to interesting header list for it to not be
1867 // nuked by the time we handle the request later.
1868 // Only required in Arduino framework.
1869 request->addInterestingHeader(HEADER_CORS_REQ_PNA);
1870#endif
1871 return true;
1872 }
1873#endif
1874
1875 UrlMatch match = match_url(request->url().c_str(), true); // NOLINT
1876 if (!match.valid)
1877 return false;
1878#ifdef USE_SENSOR
1879 if (request->method() == HTTP_GET && match.domain == "sensor")
1880 return true;
1881#endif
1882
1883#ifdef USE_SWITCH
1884 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "switch")
1885 return true;
1886#endif
1887
1888#ifdef USE_BUTTON
1889 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "button")
1890 return true;
1891#endif
1892
1893#ifdef USE_BINARY_SENSOR
1894 if (request->method() == HTTP_GET && match.domain == "binary_sensor")
1895 return true;
1896#endif
1897
1898#ifdef USE_FAN
1899 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "fan")
1900 return true;
1901#endif
1902
1903#ifdef USE_LIGHT
1904 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "light")
1905 return true;
1906#endif
1907
1908#ifdef USE_TEXT_SENSOR
1909 if (request->method() == HTTP_GET && match.domain == "text_sensor")
1910 return true;
1911#endif
1912
1913#ifdef USE_COVER
1914 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "cover")
1915 return true;
1916#endif
1917
1918#ifdef USE_NUMBER
1919 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "number")
1920 return true;
1921#endif
1922
1923#ifdef USE_DATETIME_DATE
1924 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "date")
1925 return true;
1926#endif
1927
1928#ifdef USE_DATETIME_TIME
1929 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "time")
1930 return true;
1931#endif
1932
1933#ifdef USE_DATETIME_DATETIME
1934 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "datetime")
1935 return true;
1936#endif
1937
1938#ifdef USE_TEXT
1939 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text")
1940 return true;
1941#endif
1942
1943#ifdef USE_SELECT
1944 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "select")
1945 return true;
1946#endif
1947
1948#ifdef USE_CLIMATE
1949 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "climate")
1950 return true;
1951#endif
1952
1953#ifdef USE_LOCK
1954 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "lock")
1955 return true;
1956#endif
1957
1958#ifdef USE_VALVE
1959 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "valve")
1960 return true;
1961#endif
1962
1963#ifdef USE_ALARM_CONTROL_PANEL
1964 if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.domain == "alarm_control_panel")
1965 return true;
1966#endif
1967
1968#ifdef USE_EVENT
1969 if (request->method() == HTTP_GET && match.domain == "event")
1970 return true;
1971#endif
1972
1973#ifdef USE_UPDATE
1974 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "update")
1975 return true;
1976#endif
1977
1978 return false;
1979}
1980void WebServer::handleRequest(AsyncWebServerRequest *request) {
1981 if (request->url() == "/") {
1982 this->handle_index_request(request);
1983 return;
1984 }
1985
1986#ifdef USE_ARDUINO
1987 if (request->url() == "/events") {
1988 this->events_.add_new_client(this, request);
1989 return;
1990 }
1991#endif
1992
1993#ifdef USE_WEBSERVER_CSS_INCLUDE
1994 if (request->url() == "/0.css") {
1995 this->handle_css_request(request);
1996 return;
1997 }
1998#endif
1999
2000#ifdef USE_WEBSERVER_JS_INCLUDE
2001 if (request->url() == "/0.js") {
2002 this->handle_js_request(request);
2003 return;
2004 }
2005#endif
2006
2007#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
2008 if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
2009 this->handle_pna_cors_request(request);
2010 return;
2011 }
2012#endif
2013
2014 UrlMatch match = match_url(request->url().c_str()); // NOLINT
2015#ifdef USE_SENSOR
2016 if (match.domain == "sensor") {
2017 this->handle_sensor_request(request, match);
2018 return;
2019 }
2020#endif
2021
2022#ifdef USE_SWITCH
2023 if (match.domain == "switch") {
2024 this->handle_switch_request(request, match);
2025 return;
2026 }
2027#endif
2028
2029#ifdef USE_BUTTON
2030 if (match.domain == "button") {
2031 this->handle_button_request(request, match);
2032 return;
2033 }
2034#endif
2035
2036#ifdef USE_BINARY_SENSOR
2037 if (match.domain == "binary_sensor") {
2038 this->handle_binary_sensor_request(request, match);
2039 return;
2040 }
2041#endif
2042
2043#ifdef USE_FAN
2044 if (match.domain == "fan") {
2045 this->handle_fan_request(request, match);
2046 return;
2047 }
2048#endif
2049
2050#ifdef USE_LIGHT
2051 if (match.domain == "light") {
2052 this->handle_light_request(request, match);
2053 return;
2054 }
2055#endif
2056
2057#ifdef USE_TEXT_SENSOR
2058 if (match.domain == "text_sensor") {
2059 this->handle_text_sensor_request(request, match);
2060 return;
2061 }
2062#endif
2063
2064#ifdef USE_COVER
2065 if (match.domain == "cover") {
2066 this->handle_cover_request(request, match);
2067 return;
2068 }
2069#endif
2070
2071#ifdef USE_NUMBER
2072 if (match.domain == "number") {
2073 this->handle_number_request(request, match);
2074 return;
2075 }
2076#endif
2077
2078#ifdef USE_DATETIME_DATE
2079 if (match.domain == "date") {
2080 this->handle_date_request(request, match);
2081 return;
2082 }
2083#endif
2084
2085#ifdef USE_DATETIME_TIME
2086 if (match.domain == "time") {
2087 this->handle_time_request(request, match);
2088 return;
2089 }
2090#endif
2091
2092#ifdef USE_DATETIME_DATETIME
2093 if (match.domain == "datetime") {
2094 this->handle_datetime_request(request, match);
2095 return;
2096 }
2097#endif
2098
2099#ifdef USE_TEXT
2100 if (match.domain == "text") {
2101 this->handle_text_request(request, match);
2102 return;
2103 }
2104#endif
2105
2106#ifdef USE_SELECT
2107 if (match.domain == "select") {
2108 this->handle_select_request(request, match);
2109 return;
2110 }
2111#endif
2112
2113#ifdef USE_CLIMATE
2114 if (match.domain == "climate") {
2115 this->handle_climate_request(request, match);
2116 return;
2117 }
2118#endif
2119
2120#ifdef USE_LOCK
2121 if (match.domain == "lock") {
2122 this->handle_lock_request(request, match);
2123
2124 return;
2125 }
2126#endif
2127
2128#ifdef USE_VALVE
2129 if (match.domain == "valve") {
2130 this->handle_valve_request(request, match);
2131 return;
2132 }
2133#endif
2134
2135#ifdef USE_ALARM_CONTROL_PANEL
2136 if (match.domain == "alarm_control_panel") {
2137 this->handle_alarm_control_panel_request(request, match);
2138
2139 return;
2140 }
2141#endif
2142
2143#ifdef USE_UPDATE
2144 if (match.domain == "update") {
2145 this->handle_update_request(request, match);
2146 return;
2147 }
2148#endif
2149}
2150
2152
2153void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) {
2154 this->sorting_entitys_[entity] = SortingComponents{weight, group};
2155}
2156
2157void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_name, float weight) {
2158 this->sorting_groups_[group_id] = SortingGroup{group_name, weight};
2159}
2160
2161void WebServer::schedule_(std::function<void()> &&f) {
2162#ifdef USE_ESP32
2163 xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);
2164 to_schedule_.push_back(std::move(f));
2165 xSemaphoreGive(this->to_schedule_lock_);
2166#else
2167 this->defer(std::move(f));
2168#endif
2169}
2170
2171} // namespace web_server
2172} // namespace esphome
2173#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:58
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:204
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:62
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:242
std::string get_use_address()
Get the active network hostname.
Definition util.cpp:65
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:314
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
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:323
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
@ PARSE_ON
Definition helpers.h:443
@ PARSE_TOGGLE
Definition helpers.h:445
@ PARSE_OFF
Definition helpers.h:444
@ PARSE_NONE
Definition helpers.h:442
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