From 2b8fdfb6a636ab4ad159453d9ac99c3b0adcf474 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Oct 2025 05:22:15 -1000 Subject: [PATCH 1/4] [web_server] Reduce code duplication in JSON generation with helper functions --- esphome/components/web_server/web_server.cpp | 32 +++++--------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index cfd5fc947b..91105ae826 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -454,12 +454,8 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail const auto uom_ref = obj->get_unit_of_measurement_ref(); // Build JSON directly inline - std::string state; - if (std::isnan(value)) { - state = "NA"; - } else { - state = value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref); - } + std::string state = + std::isnan(value) ? "NA" : value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref); set_json_icon_state_value(root, obj, "sensor", state, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); @@ -1020,10 +1016,8 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "date", start_config); std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day); - root["value"] = value; - root["state"] = value; + set_json_icon_state_value(root, obj, "date", value, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); } @@ -1078,10 +1072,8 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "time", start_config); std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second); - root["value"] = value; - root["state"] = value; + set_json_icon_state_value(root, obj, "time", value, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); } @@ -1136,11 +1128,9 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "datetime", start_config); std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second); - root["value"] = value; - root["state"] = value; + set_json_icon_state_value(root, obj, "datetime", value, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); } @@ -1191,16 +1181,11 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "text", start_config); + set_json_value(root, obj, "text", value, start_config); root["min_length"] = obj->traits.get_min_length(); root["max_length"] = obj->traits.get_max_length(); root["pattern"] = obj->traits.get_pattern(); - if (obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD) { - root["state"] = "********"; - } else { - root["state"] = value; - } - root["value"] = value; + root["state"] = obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD ? "********" : value; if (start_config == DETAIL_ALL) { root["mode"] = (int) obj->traits.get_mode(); this->add_sorting_info_(root, obj); @@ -1754,8 +1739,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "update", start_config); - root["value"] = obj->update_info.latest_version; + set_json_value(root, obj, "update", obj->update_info.latest_version, start_config); root["state"] = update_state_to_string(obj->state); if (start_config == DETAIL_ALL) { root["current_version"] = obj->update_info.current_version; From fed252d1d32099290c653b54ac36014a51dd984c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Oct 2025 05:40:31 -1000 Subject: [PATCH 2/4] wip --- esphome/components/web_server/web_server.cpp | 26 +++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 91105ae826..cd66368696 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -453,10 +453,9 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail const auto uom_ref = obj->get_unit_of_measurement_ref(); - // Build JSON directly inline - std::string state = + set_json_value(root, obj, "sensor", value, start_config); + root["state"] = std::isnan(value) ? "NA" : value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref); - set_json_icon_state_value(root, obj, "sensor", state, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); if (!uom_ref.empty()) @@ -796,8 +795,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "light", start_config); - root["state"] = obj->remote_values.is_on() ? "ON" : "OFF"; + set_json_value(root, obj, "light", obj->remote_values.is_on() ? "ON" : "OFF", start_config); light::LightJSONSchema::dump_json(*obj, root); if (start_config == DETAIL_ALL) { @@ -945,6 +943,12 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail const auto uom_ref = obj->traits.get_unit_of_measurement_ref(); set_json_id(root, obj, "number", start_config); + root["value"] = std::isnan(value) + ? "\"NaN\"" + : value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); + root["state"] = std::isnan(value) ? "NA" + : value_accuracy_with_uom_to_string( + value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref); if (start_config == DETAIL_ALL) { root["min_value"] = value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step())); @@ -956,14 +960,6 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["uom"] = uom_ref; this->add_sorting_info_(root, obj); } - if (std::isnan(value)) { - root["value"] = "\"NaN\""; - root["state"] = "NA"; - } else { - root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); - root["state"] = - value_accuracy_with_uom_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref); - } return builder.serialize(); } @@ -1739,8 +1735,8 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_value(root, obj, "update", obj->update_info.latest_version, start_config); - root["state"] = update_state_to_string(obj->state); + set_json_icon_state_value(root, obj, "update", update_state_to_string(obj->state), obj->update_info.latest_version, + start_config); if (start_config == DETAIL_ALL) { root["current_version"] = obj->update_info.current_version; root["title"] = obj->update_info.title; From 41d07701eeb03e90534079c84b926f5e08c3f074 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Oct 2025 05:46:20 -1000 Subject: [PATCH 3/4] tweak --- esphome/components/web_server/web_server.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index cd66368696..194a7158a5 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -453,9 +453,9 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail const auto uom_ref = obj->get_unit_of_measurement_ref(); - set_json_value(root, obj, "sensor", value, start_config); - root["state"] = + std::string state = std::isnan(value) ? "NA" : value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref); + set_json_icon_state_value(root, obj, "sensor", state, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); if (!uom_ref.empty()) @@ -942,13 +942,13 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail const auto uom_ref = obj->traits.get_unit_of_measurement_ref(); - set_json_id(root, obj, "number", start_config); - root["value"] = std::isnan(value) - ? "\"NaN\"" - : value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); - root["state"] = std::isnan(value) ? "NA" - : value_accuracy_with_uom_to_string( - value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref); + std::string val_str = std::isnan(value) + ? "\"NaN\"" + : value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); + std::string state_str = std::isnan(value) ? "NA" + : value_accuracy_with_uom_to_string( + value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref); + set_json_icon_state_value(root, obj, "number", state_str, val_str, start_config); if (start_config == DETAIL_ALL) { root["min_value"] = value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step())); From 9ac48b162b32bc987d6266c0f3abe454fe22711a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Oct 2025 05:48:56 -1000 Subject: [PATCH 4/4] tweak --- esphome/components/web_server/web_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 194a7158a5..648a02d89b 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1177,11 +1177,11 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_value(root, obj, "text", value, start_config); + std::string state = obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD ? "********" : value; + set_json_icon_state_value(root, obj, "text", state, value, start_config); root["min_length"] = obj->traits.get_min_length(); root["max_length"] = obj->traits.get_max_length(); root["pattern"] = obj->traits.get_pattern(); - root["state"] = obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD ? "********" : value; if (start_config == DETAIL_ALL) { root["mode"] = (int) obj->traits.get_mode(); this->add_sorting_info_(root, obj);