12#include "StreamString.h"
29#ifdef USE_WEBSERVER_LOCAL
30#if USE_WEBSERVER_VERSION == 2
32#elif USE_WEBSERVER_VERSION == 3
40static const char *
const TAG =
"web_server";
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";
52 size_t domain_end = url.find(
'/', 1);
53 if (domain_end == std::string::npos)
55 match.
domain = url.substr(1, domain_end - 1);
60 if (url.length() == domain_end - 1)
62 size_t id_begin = domain_end + 1;
63 size_t id_end = url.find(
'/', id_begin);
65 if (id_end == std::string::npos) {
66 match.
id = url.substr(id_begin, url.length() - id_begin);
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);
78 DeferredEvent item(source, message_generator);
80 auto iter = std::find_if(this->
deferred_queue_.begin(), this->deferred_queue_.end(),
81 [&item](
const DeferredEvent &test) ->
bool { return test == item; });
93 std::string message = de.message_generator_(
web_server_, de.source_);
94 if (this->try_send(message.c_str(),
"state")) {
116 if (source ==
nullptr)
118 if (event_type ==
nullptr)
120 if (message_generator ==
nullptr)
123 if (0 != strcmp(event_type,
"state_detail_all") && 0 != strcmp(event_type,
"state")) {
124 ESP_LOGE(TAG,
"Can't defer non-state event");
133 std::string message = message_generator(
web_server_, source);
134 if (!this->try_send(message.c_str(),
"state")) {
142 uint32_t reconnect) {
143 this->send(message, event,
id, reconnect);
160 uint32_t reconnect) {
170 es->onConnect([
this, ws, es](AsyncEventSourceClient *client) {
174 es->onDisconnect([
this, ws](AsyncEventSource *source, AsyncEventSourceClient *client) {
178 es->handleRequest(request);
189 root[
"name"] = group.second.name;
190 root[
"sorting_weight"] = group.second.weight;
209 this->remove(source);
220#ifdef USE_WEBSERVER_CSS_INCLUDE
223#ifdef USE_WEBSERVER_JS_INCLUDE
238 ESP_LOGCONFIG(TAG,
"Setting up web server...");
246 [
this](
int level,
const char *tag,
const char *message) {
267 std::function<void()> fn;
285 ESP_LOGCONFIG(TAG,
"Web Server:");
290#ifdef USE_WEBSERVER_LOCAL
292 AsyncWebServerResponse *response = request->beginResponse_P(200,
"text/html", INDEX_GZ,
sizeof(INDEX_GZ));
293 response->addHeader(
"Content-Encoding",
"gzip");
294 request->send(response);
296#elif USE_WEBSERVER_VERSION >= 2
298 AsyncWebServerResponse *response =
301 request->send(response);
305#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
307 AsyncWebServerResponse *response = request->beginResponse(200,
"");
308 response->addHeader(HEADER_CORS_ALLOW_PNA,
"true");
309 response->addHeader(HEADER_PNA_NAME,
App.
get_name().c_str());
311 response->addHeader(HEADER_PNA_ID, mac.c_str());
312 request->send(response);
316#ifdef USE_WEBSERVER_CSS_INCLUDE
318 AsyncWebServerResponse *response =
320 response->addHeader(
"Content-Encoding",
"gzip");
321 request->send(response);
325#ifdef USE_WEBSERVER_JS_INCLUDE
327 AsyncWebServerResponse *response =
329 response->addHeader(
"Content-Encoding",
"gzip");
330 request->send(response);
334#define set_json_id(root, obj, sensor, start_config) \
335 (root)["id"] = sensor; \
336 if (((start_config) == DETAIL_ALL)) { \
337 (root)["name"] = (obj)->get_name(); \
338 (root)["icon"] = (obj)->get_icon(); \
339 (root)["entity_category"] = (obj)->get_entity_category(); \
340 if ((obj)->is_disabled_by_default()) \
341 (root)["is_disabled_by_default"] = (obj)->is_disabled_by_default(); \
344#define set_json_value(root, obj, sensor, value, start_config) \
345 set_json_id((root), (obj), sensor, start_config); \
346 (root)["value"] = value;
348#define set_json_icon_state_value(root, obj, sensor, state, value, start_config) \
349 set_json_value(root, obj, sensor, value, start_config); \
350 (root)["state"] = state;
360 if (obj->get_object_id() != match.
id)
362 if (request->method() == HTTP_GET && match.
method.empty()) {
364 auto *param = request->getParam(
"detail");
365 if (param && param->value() ==
"all") {
368 std::string data = this->
sensor_json(obj, obj->state, detail);
369 request->send(200,
"application/json", data.c_str());
384 if (std::isnan(value)) {
391 set_json_icon_state_value(root, obj,
"sensor-" + obj->
get_object_id(),
state, value, start_config);
395 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
406#ifdef USE_TEXT_SENSOR
414 if (obj->get_object_id() != match.
id)
416 if (request->method() == HTTP_GET && match.
method.empty()) {
418 auto *param = request->getParam(
"detail");
419 if (param && param->value() ==
"all") {
423 request->send(200,
"application/json", data.c_str());
440 set_json_icon_state_value(root, obj,
"text_sensor-" + obj->
get_object_id(), value, value, start_config);
444 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
461 if (obj->get_object_id() != match.
id)
464 if (request->method() == HTTP_GET && match.
method.empty()) {
466 auto *param = request->getParam(
"detail");
467 if (param && param->value() ==
"all") {
470 std::string data = this->
switch_json(obj, obj->state, detail);
471 request->send(200,
"application/json", data.c_str());
472 }
else if (match.
method ==
"toggle") {
473 this->
schedule_([obj]() { obj->toggle(); });
475 }
else if (match.
method ==
"turn_on") {
476 this->
schedule_([obj]() { obj->turn_on(); });
478 }
else if (match.
method ==
"turn_off") {
479 this->
schedule_([obj]() { obj->turn_off(); });
496 set_json_icon_state_value(root, obj,
"switch-" + obj->
get_object_id(), value ?
"ON" :
"OFF", value, start_config);
501 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
513 if (obj->get_object_id() != match.
id)
515 if (request->method() == HTTP_GET && match.
method.empty()) {
517 auto *param = request->getParam(
"detail");
518 if (param && param->value() ==
"all") {
522 request->send(200,
"application/json", data.c_str());
523 }
else if (match.
method ==
"press") {
524 this->
schedule_([obj]() { obj->press(); });
542 set_json_id(root, obj,
"button-" + obj->
get_object_id(), start_config);
546 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
555#ifdef USE_BINARY_SENSOR
563 if (obj->get_object_id() != match.
id)
565 if (request->method() == HTTP_GET && match.
method.empty()) {
567 auto *param = request->getParam(
"detail");
568 if (param && param->value() ==
"all") {
572 request->send(200,
"application/json", data.c_str());
588 set_json_icon_state_value(root, obj,
"binary_sensor-" + obj->
get_object_id(), value ?
"ON" :
"OFF", value,
593 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
610 if (obj->get_object_id() != match.
id)
613 if (request->method() == HTTP_GET && match.
method.empty()) {
615 auto *param = request->getParam(
"detail");
616 if (param && param->value() ==
"all") {
619 std::string data = this->
fan_json(obj, detail);
620 request->send(200,
"application/json", data.c_str());
621 }
else if (match.
method ==
"toggle") {
622 this->
schedule_([obj]() { obj->toggle().perform(); });
624 }
else if (match.
method ==
"turn_on" || match.
method ==
"turn_off") {
625 auto call = match.
method ==
"turn_on" ? obj->turn_on() : obj->turn_off();
627 if (request->hasParam(
"speed_level")) {
628 auto speed_level = request->getParam(
"speed_level")->value();
630 if (!
val.has_value()) {
631 ESP_LOGW(TAG,
"Can't convert '%s' to number!", speed_level.c_str());
636 if (request->hasParam(
"oscillation")) {
637 auto speed = request->getParam(
"oscillation")->value();
641 call.set_oscillating(
true);
644 call.set_oscillating(
false);
647 call.set_oscillating(!obj->oscillating);
674 if (traits.supports_speed()) {
675 root[
"speed_level"] = obj->
speed;
676 root[
"speed_count"] = traits.supported_speed_count();
683 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
700 if (obj->get_object_id() != match.
id)
703 if (request->method() == HTTP_GET && match.
method.empty()) {
705 auto *param = request->getParam(
"detail");
706 if (param && param->value() ==
"all") {
709 std::string data = this->
light_json(obj, detail);
710 request->send(200,
"application/json", data.c_str());
711 }
else if (match.
method ==
"toggle") {
712 this->
schedule_([obj]() { obj->toggle().perform(); });
714 }
else if (match.
method ==
"turn_on") {
715 auto call = obj->turn_on();
716 if (request->hasParam(
"brightness")) {
718 if (brightness.has_value()) {
719 call.set_brightness(*brightness / 255.0f);
722 if (request->hasParam(
"r")) {
725 call.set_red(*r / 255.0f);
728 if (request->hasParam(
"g")) {
731 call.set_green(*g / 255.0f);
734 if (request->hasParam(
"b")) {
737 call.set_blue(*b / 255.0f);
740 if (request->hasParam(
"white_value")) {
742 if (white_value.has_value()) {
743 call.set_white(*white_value / 255.0f);
746 if (request->hasParam(
"color_temp")) {
748 if (color_temp.has_value()) {
749 call.set_color_temperature(*color_temp);
752 if (request->hasParam(
"flash")) {
754 if (flash.has_value()) {
755 call.set_flash_length(*flash * 1000);
758 if (request->hasParam(
"transition")) {
760 if (transition.has_value()) {
761 call.set_transition_length(*transition * 1000);
764 if (request->hasParam(
"effect")) {
765 const char *effect = request->getParam(
"effect")->value().c_str();
766 call.set_effect(effect);
771 }
else if (match.
method ==
"turn_off") {
772 auto call = obj->turn_off();
773 if (request->hasParam(
"transition")) {
775 if (transition.has_value()) {
776 call.set_transition_length(*transition * 1000);
796 set_json_id(root, obj,
"light-" + obj->
get_object_id(), start_config);
801 JsonArray opt = root.createNestedArray(
"effects");
804 opt.add(option->get_name());
808 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
825 if (obj->get_object_id() != match.
id)
828 if (request->method() == HTTP_GET && match.
method.empty()) {
830 auto *param = request->getParam(
"detail");
831 if (param && param->value() ==
"all") {
834 std::string data = this->
cover_json(obj, detail);
835 request->send(200,
"application/json", data.c_str());
839 auto call = obj->make_call();
840 if (match.
method ==
"open") {
841 call.set_command_open();
842 }
else if (match.
method ==
"close") {
843 call.set_command_close();
844 }
else if (match.
method ==
"stop") {
845 call.set_command_stop();
846 }
else if (match.
method ==
"toggle") {
847 call.set_command_toggle();
848 }
else if (match.
method !=
"set") {
853 auto traits = obj->get_traits();
854 if ((request->hasParam(
"position") && !traits.get_supports_position()) ||
855 (request->hasParam(
"tilt") && !traits.get_supports_tilt())) {
860 if (request->hasParam(
"position")) {
866 if (request->hasParam(
"tilt")) {
868 if (
tilt.has_value()) {
894 root[
"tilt"] = obj->
tilt;
898 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
915 if (obj->get_object_id() != match.
id)
918 if (request->method() == HTTP_GET && match.
method.empty()) {
920 auto *param = request->getParam(
"detail");
921 if (param && param->value() ==
"all") {
924 std::string data = this->
number_json(obj, obj->state, detail);
925 request->send(200,
"application/json", data.c_str());
928 if (match.
method !=
"set") {
933 auto call = obj->make_call();
934 if (request->hasParam(
"value")) {
936 if (value.has_value())
937 call.set_value(*value);
955 set_json_id(root, obj,
"number-" + obj->
get_object_id(), start_config);
968 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
973 if (std::isnan(value)) {
974 root[
"value"] =
"\"NaN\"";
975 root[
"state"] =
"NA";
981 root[
"state"] =
state;
987#ifdef USE_DATETIME_DATE
995 if (obj->get_object_id() != match.
id)
997 if (request->method() == HTTP_GET && match.
method.empty()) {
999 auto *param = request->getParam(
"detail");
1000 if (param && param->value() ==
"all") {
1003 std::string data = this->
date_json(obj, detail);
1004 request->send(200,
"application/json", data.c_str());
1007 if (match.
method !=
"set") {
1012 auto call = obj->make_call();
1014 if (!request->hasParam(
"value")) {
1019 if (request->hasParam(
"value")) {
1020 std::string value = request->getParam(
"value")->value().c_str();
1021 call.set_date(value);
1039 set_json_id(root, obj,
"date-" + obj->
get_object_id(), start_config);
1041 root[
"value"] = value;
1042 root[
"state"] = value;
1046 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1055#ifdef USE_DATETIME_TIME
1063 if (obj->get_object_id() != match.
id)
1065 if (request->method() == HTTP_GET && match.
method.empty()) {
1067 auto *param = request->getParam(
"detail");
1068 if (param && param->value() ==
"all") {
1071 std::string data = this->
time_json(obj, detail);
1072 request->send(200,
"application/json", data.c_str());
1075 if (match.
method !=
"set") {
1080 auto call = obj->make_call();
1082 if (!request->hasParam(
"value")) {
1087 if (request->hasParam(
"value")) {
1088 std::string value = request->getParam(
"value")->value().c_str();
1089 call.set_time(value);
1106 set_json_id(root, obj,
"time-" + obj->
get_object_id(), start_config);
1108 root[
"value"] = value;
1109 root[
"state"] = value;
1113 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1122#ifdef USE_DATETIME_DATETIME
1130 if (obj->get_object_id() != match.
id)
1132 if (request->method() == HTTP_GET && match.
method.empty()) {
1134 auto *param = request->getParam(
"detail");
1135 if (param && param->value() ==
"all") {
1139 request->send(200,
"application/json", data.c_str());
1142 if (match.
method !=
"set") {
1147 auto call = obj->make_call();
1149 if (!request->hasParam(
"value")) {
1154 if (request->hasParam(
"value")) {
1155 std::string value = request->getParam(
"value")->value().c_str();
1156 call.set_datetime(value);
1173 set_json_id(root, obj,
"datetime-" + obj->
get_object_id(), start_config);
1176 root[
"value"] = value;
1177 root[
"state"] = value;
1181 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1198 if (obj->get_object_id() != match.
id)
1201 if (request->method() == HTTP_GET && match.
method.empty()) {
1203 auto *param = request->getParam(
"detail");
1204 if (param && param->value() ==
"all") {
1207 std::string data = this->
text_json(obj, obj->state, detail);
1208 request->send(200,
"application/json", data.c_str());
1211 if (match.
method !=
"set") {
1216 auto call = obj->make_call();
1217 if (request->hasParam(
"value")) {
1218 String value = request->getParam(
"value")->value();
1219 call.set_value(value.c_str());
1236 return json::build_json([
this, obj, value, start_config](JsonObject root) {
1237 set_json_id(root, obj,
"text-" + obj->
get_object_id(), start_config);
1242 root[
"state"] =
"********";
1244 root[
"state"] = value;
1246 root[
"value"] = value;
1251 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1268 if (obj->get_object_id() != match.
id)
1271 if (request->method() == HTTP_GET && match.
method.empty()) {
1273 auto *param = request->getParam(
"detail");
1274 if (param && param->value() ==
"all") {
1277 std::string data = this->
select_json(obj, obj->state, detail);
1278 request->send(200,
"application/json", data.c_str());
1282 if (match.
method !=
"set") {
1287 auto call = obj->make_call();
1289 if (request->hasParam(
"option")) {
1290 auto option = request->getParam(
"option")->value();
1291 call.set_option(option.c_str());
1307 return json::build_json([
this, obj, value, start_config](JsonObject root) {
1308 set_json_icon_state_value(root, obj,
"select-" + obj->
get_object_id(), value, value, start_config);
1310 JsonArray opt = root.createNestedArray(
"option");
1316 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1326#define PSTR_LOCAL(mode_s) strncpy_P(buf, (PGM_P) ((mode_s)), 15)
1336 if (obj->get_object_id() != match.
id)
1339 if (request->method() == HTTP_GET && match.
method.empty()) {
1341 auto *param = request->getParam(
"detail");
1342 if (param && param->value() ==
"all") {
1346 request->send(200,
"application/json", data.c_str());
1350 if (match.
method !=
"set") {
1355 auto call = obj->make_call();
1357 if (request->hasParam(
"mode")) {
1358 auto mode = request->getParam(
"mode")->value();
1362 if (request->hasParam(
"fan_mode")) {
1363 auto mode = request->getParam(
"fan_mode")->value();
1367 if (request->hasParam(
"swing_mode")) {
1368 auto mode = request->getParam(
"swing_mode")->value();
1372 if (request->hasParam(
"target_temperature_high")) {
1378 if (request->hasParam(
"target_temperature_low")) {
1384 if (request->hasParam(
"target_temperature")) {
1404 set_json_id(root, obj,
"climate-" + obj->
get_object_id(), start_config);
1407 int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
1411 JsonArray opt = root.createNestedArray(
"modes");
1414 if (!traits.get_supported_custom_fan_modes().empty()) {
1415 JsonArray opt = root.createNestedArray(
"fan_modes");
1420 if (!traits.get_supported_custom_fan_modes().empty()) {
1421 JsonArray opt = root.createNestedArray(
"custom_fan_modes");
1422 for (
auto const &
custom_fan_mode : traits.get_supported_custom_fan_modes())
1425 if (traits.get_supports_swing_modes()) {
1426 JsonArray opt = root.createNestedArray(
"swing_modes");
1427 for (
auto swing_mode : traits.get_supported_swing_modes())
1431 JsonArray opt = root.createNestedArray(
"presets");
1436 JsonArray opt = root.createNestedArray(
"custom_presets");
1437 for (
auto const &
custom_preset : traits.get_supported_custom_presets())
1442 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1448 bool has_state =
false;
1449 root[
"mode"] = PSTR_LOCAL(climate_mode_to_string(obj->
mode));
1452 root[
"step"] = traits.get_visual_target_temperature_step();
1453 if (traits.get_supports_action()) {
1454 root[
"action"] = PSTR_LOCAL(climate_action_to_string(obj->
action));
1455 root[
"state"] = root[
"action"];
1459 root[
"fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->
fan_mode.
value()));
1465 root[
"preset"] = PSTR_LOCAL(climate_preset_to_string(obj->
preset.
value()));
1470 if (traits.get_supports_swing_modes()) {
1471 root[
"swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->
swing_mode));
1473 if (traits.get_supports_current_temperature()) {
1477 root[
"current_temperature"] =
"NA";
1480 if (traits.get_supports_two_point_target_temperature()) {
1490 root[
"state"] = root[
"target_temperature"];
1504 if (obj->get_object_id() != match.
id)
1507 if (request->method() == HTTP_GET && match.
method.empty()) {
1509 auto *param = request->getParam(
"detail");
1510 if (param && param->value() ==
"all") {
1513 std::string data = this->
lock_json(obj, obj->state, detail);
1514 request->send(200,
"application/json", data.c_str());
1515 }
else if (match.
method ==
"lock") {
1516 this->
schedule_([obj]() { obj->lock(); });
1518 }
else if (match.
method ==
"unlock") {
1519 this->
schedule_([obj]() { obj->unlock(); });
1521 }
else if (match.
method ==
"open") {
1522 this->
schedule_([obj]() { obj->open(); });
1538 return json::build_json([
this, obj, value, start_config](JsonObject root) {
1544 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1561 if (obj->get_object_id() != match.
id)
1564 if (request->method() == HTTP_GET && match.
method.empty()) {
1566 auto *param = request->getParam(
"detail");
1567 if (param && param->value() ==
"all") {
1570 std::string data = this->
valve_json(obj, detail);
1571 request->send(200,
"application/json", data.c_str());
1575 auto call = obj->make_call();
1576 if (match.
method ==
"open") {
1577 call.set_command_open();
1578 }
else if (match.
method ==
"close") {
1579 call.set_command_close();
1580 }
else if (match.
method ==
"stop") {
1581 call.set_command_stop();
1582 }
else if (match.
method ==
"toggle") {
1583 call.set_command_toggle();
1584 }
else if (match.
method !=
"set") {
1589 auto traits = obj->get_traits();
1590 if (request->hasParam(
"position") && !traits.get_supports_position()) {
1595 if (request->hasParam(
"position")) {
1625 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1634#ifdef USE_ALARM_CONTROL_PANEL
1642 if (obj->get_object_id() != match.
id)
1645 if (request->method() == HTTP_GET && match.
method.empty()) {
1647 auto *param = request->getParam(
"detail");
1648 if (param && param->value() ==
"all") {
1652 request->send(200,
"application/json", data.c_str());
1656 auto call = obj->make_call();
1657 if (request->hasParam(
"code")) {
1658 call.set_code(request->getParam(
"code")->value().c_str());
1661 if (match.
method ==
"disarm") {
1663 }
else if (match.
method ==
"arm_away") {
1665 }
else if (match.
method ==
"arm_home") {
1667 }
else if (match.
method ==
"arm_night") {
1669 }
else if (match.
method ==
"arm_vacation") {
1670 call.arm_vacation();
1695 return json::build_json([
this, obj, value, start_config](JsonObject root) {
1697 set_json_icon_state_value(root, obj,
"alarm-control-panel-" + obj->
get_object_id(),
1698 PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
1702 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1718 if (obj->get_object_id() != match.
id)
1721 if (request->method() == HTTP_GET && match.
method.empty()) {
1723 auto *param = request->getParam(
"detail");
1724 if (param && param->value() ==
"all") {
1727 std::string data = this->
event_json(obj,
"", detail);
1728 request->send(200,
"application/json", data.c_str());
1743 return json::build_json([
this, obj, event_type, start_config](JsonObject root) {
1744 set_json_id(root, obj,
"event-" + obj->
get_object_id(), start_config);
1745 if (!event_type.empty()) {
1746 root[
"event_type"] = event_type;
1749 JsonArray event_types = root.createNestedArray(
"event_types");
1751 event_types.add(event_type);
1756 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1773 if (obj->get_object_id() != match.
id)
1776 if (request->method() == HTTP_GET && match.
method.empty()) {
1778 auto *param = request->getParam(
"detail");
1779 if (param && param->value() ==
"all") {
1782 std::string data = this->
update_json(obj, detail);
1783 request->send(200,
"application/json", data.c_str());
1787 if (match.
method !=
"install") {
1792 this->
schedule_([obj]()
mutable { obj->perform(); });
1806 set_json_id(root, obj,
"update-" + obj->
get_object_id(), start_config);
1808 switch (obj->
state) {
1810 root[
"state"] =
"NO UPDATE";
1813 root[
"state"] =
"UPDATE AVAILABLE";
1816 root[
"state"] =
"INSTALLING";
1819 root[
"state"] =
"UNKNOWN";
1829 if (this->
sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1839 if (request->url() ==
"/")
1843 if (request->url() ==
"/events") {
1848#ifdef USE_WEBSERVER_CSS_INCLUDE
1849 if (request->url() ==
"/0.css")
1853#ifdef USE_WEBSERVER_JS_INCLUDE
1854 if (request->url() ==
"/0.js")
1858#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
1859 if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
1864 request->addInterestingHeader(HEADER_CORS_REQ_PNA);
1874 if (request->method() == HTTP_GET && match.
domain ==
"sensor")
1879 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"switch")
1884 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"button")
1888#ifdef USE_BINARY_SENSOR
1889 if (request->method() == HTTP_GET && match.
domain ==
"binary_sensor")
1894 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"fan")
1899 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"light")
1903#ifdef USE_TEXT_SENSOR
1904 if (request->method() == HTTP_GET && match.
domain ==
"text_sensor")
1909 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"cover")
1914 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"number")
1918#ifdef USE_DATETIME_DATE
1919 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"date")
1923#ifdef USE_DATETIME_TIME
1924 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"time")
1928#ifdef USE_DATETIME_DATETIME
1929 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"datetime")
1934 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"text")
1939 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"select")
1944 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"climate")
1949 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"lock")
1954 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"valve")
1958#ifdef USE_ALARM_CONTROL_PANEL
1959 if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.
domain ==
"alarm_control_panel")
1964 if (request->method() == HTTP_GET && match.
domain ==
"event")
1969 if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.
domain ==
"update")
1976 if (request->url() ==
"/") {
1982 if (request->url() ==
"/events") {
1988#ifdef USE_WEBSERVER_CSS_INCLUDE
1989 if (request->url() ==
"/0.css") {
1995#ifdef USE_WEBSERVER_JS_INCLUDE
1996 if (request->url() ==
"/0.js") {
2002#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
2003 if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
2011 if (match.
domain ==
"sensor") {
2018 if (match.
domain ==
"switch") {
2025 if (match.
domain ==
"button") {
2031#ifdef USE_BINARY_SENSOR
2032 if (match.
domain ==
"binary_sensor") {
2039 if (match.
domain ==
"fan") {
2046 if (match.
domain ==
"light") {
2052#ifdef USE_TEXT_SENSOR
2053 if (match.
domain ==
"text_sensor") {
2060 if (match.
domain ==
"cover") {
2067 if (match.
domain ==
"number") {
2073#ifdef USE_DATETIME_DATE
2074 if (match.
domain ==
"date") {
2080#ifdef USE_DATETIME_TIME
2081 if (match.
domain ==
"time") {
2087#ifdef USE_DATETIME_DATETIME
2088 if (match.
domain ==
"datetime") {
2095 if (match.
domain ==
"text") {
2102 if (match.
domain ==
"select") {
2109 if (match.
domain ==
"climate") {
2116 if (match.
domain ==
"lock") {
2124 if (match.
domain ==
"valve") {
2130#ifdef USE_ALARM_CONTROL_PANEL
2131 if (match.
domain ==
"alarm_control_panel") {
2139 if (match.
domain ==
"update") {
2162 this->
defer(std::move(f));
BedjetMode mode
BedJet operating mode.
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.
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)
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.
ClimateDevice - This is the base class for all climate integrations.
ClimateMode mode
The active mode of the climate device.
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
ClimateTraits get_traits()
Get the traits of this climate device with all overrides applied.
float target_temperature
The target temperature of the climate device.
optional< std::string > custom_fan_mode
The active custom fan mode of the climate device.
ClimateSwingMode swing_mode
The active swing mode of the climate device.
float target_temperature_low
The minimum target temperature of the climate device, for climate devices with split target temperatu...
optional< std::string > custom_preset
The active custom preset mode of the climate device.
float current_temperature
The current temperature of the climate device, as reported from the integration.
ClimateAction action
The active state of the climate device.
optional< ClimatePreset > preset
The active preset of the climate device.
float target_temperature_high
The maximum target temperature of the climate device, for climate devices with split target temperatu...
int8_t get_target_temperature_accuracy_decimals() const
Base class for all cover devices.
CoverOperation current_operation
The current operation of the cover (idle, opening, closing).
float tilt
The current tilt value of the cover from 0.0 to 1.0.
float position
The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
bool is_fully_closed() const
Helper method to check if the cover is fully closed. Equivalent to comparing .position against 0....
virtual CoverTraits get_traits()=0
bool get_supports_position() const
bool get_supports_tilt() const
std::set< std::string > get_event_types() const
virtual FanTraits get_traits()=0
bool oscillating
The current oscillation state of the fan.
bool state
The current on/off state of the fan.
int speed
The current fan speed level.
bool supports_oscillation() const
Return if this fan supports oscillation.
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...
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.
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.
Base-class for all numbers.
float get_min_value() const
float get_max_value() const
NumberMode get_mode() const
value_type const & value() const
Base-class for all selects.
std::vector< std::string > get_options() const
Base-class for all sensors.
int8_t get_accuracy_decimals()
Get the accuracy in decimals, using the manual override if set.
Base class for all switches.
virtual bool assumed_state()
Return whether this switch uses an assumed state - i.e.
Base-class for all text inputs.
TextMode get_mode() const
int get_max_length() const
std::string get_pattern() const
int get_min_length() const
const UpdateState & state
const UpdateInfo & update_info
Base class for all valve devices.
bool is_fully_closed() const
Helper method to check if the valve is fully closed. Equivalent to comparing .position against 0....
float position
The position of the valve from 0.0 (fully closed) to 1.0 (fully open).
ValveOperation current_operation
The current operation of the valve (idle, opening, closing).
virtual ValveTraits get_traits()=0
bool get_supports_position() const
void process_deferred_queue_()
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)
ListEntitiesIterator entities_iterator_
std::vector< DeferredEvent > deferred_queue_
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.
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_
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_
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>'.
void dump_config() override
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_
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_
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_
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_
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)
const char * css_include_
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)
uint16_t get_port() const
float target_temperature_high
ClimateSwingMode swing_mode
float target_temperature_low
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)
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
LockState
Enum for all states a lock can be in.
const char * lock_state_to_string(LockState state)
std::string get_use_address()
Get the active network hostname.
@ UPDATE_STATE_INSTALLING
const char * valve_operation_to_str(ValveOperation op)
std::string(WebServer *, void *) message_generator_t
UrlMatch match_url(const std::string &url, bool only_domain=false)
Providing packet encoding functions for exchanging data with a remote host.
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals)
Create a string from a value and an accuracy in decimals.
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off)
Parse a string that contains either on, off or toggle.
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
int8_t step_to_accuracy_decimals(float step)
Derive accuracy in decimals from an increment step.
std::string str_sprintf(const char *fmt,...)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
std::string current_version
std::string latest_version
Internal helper struct that is used to parse incoming URLs.
bool valid
Whether this match is valid.
std::string domain
The domain of the component, for example "sensor".
std::string id
The id of the device that's being accessed, for example "living_room_fan".
std::string method
The method that's being called, for example "turn_on".
friend class DeferredUpdateEventSource
const size_t ESPHOME_WEBSERVER_INDEX_HTML_SIZE
const size_t ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE
const size_t ESPHOME_WEBSERVER_JS_INCLUDE_SIZE