From f92725f76e38adc07352c69d3e5379d411d2183c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 Feb 2026 17:54:56 -0600 Subject: [PATCH] Eliminate double query lookups and unify numeric parse helpers - Mark find_query_value_ as const - Remove redundant hasArg() guards where parse_number() already handles empty strings (returns nullopt) - Use !empty() instead of hasArg() for parse_bool_param_ - Merge parse_float_param_ and parse_int_param_ into single parse_num_param_ template - Combine missing/empty checks for IR data parameter --- esphome/components/web_server/web_server.cpp | 42 ++++++++--------- esphome/components/web_server/web_server.h | 45 ++++++------------- .../web_server_idf/web_server_idf.cpp | 2 +- .../web_server_idf/web_server_idf.h | 2 +- 4 files changed, 34 insertions(+), 57 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index eced4cddd2..3504f3ee02 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -860,7 +860,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc } auto call = is_on ? obj->turn_on() : obj->turn_off(); - parse_int_param_(request, ESPHOME_F("speed_level"), call, &decltype(call)::set_speed); + parse_num_param_(request, ESPHOME_F("speed_level"), call, &decltype(call)::set_speed); if (request->hasArg(ESPHOME_F("oscillation"))) { auto speed = request->arg(ESPHOME_F("oscillation")); @@ -1045,8 +1045,8 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa return; } - parse_float_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position); - parse_float_param_(request, ESPHOME_F("tilt"), call, &decltype(call)::set_tilt); + parse_num_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position); + parse_num_param_(request, ESPHOME_F("tilt"), call, &decltype(call)::set_tilt); DEFER_ACTION(call, call.perform()); request->send(200); @@ -1105,7 +1105,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM } auto call = obj->make_call(); - parse_float_param_(request, ESPHOME_F("value"), call, &decltype(call)::set_value); + parse_num_param_(request, ESPHOME_F("value"), call, &decltype(call)::set_value); DEFER_ACTION(call, call.perform()); request->send(200); @@ -1476,10 +1476,9 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url parse_string_param_(request, ESPHOME_F("swing_mode"), call, &decltype(call)::set_swing_mode); // Parse temperature parameters - parse_float_param_(request, ESPHOME_F("target_temperature_high"), call, - &decltype(call)::set_target_temperature_high); - parse_float_param_(request, ESPHOME_F("target_temperature_low"), call, &decltype(call)::set_target_temperature_low); - parse_float_param_(request, ESPHOME_F("target_temperature"), call, &decltype(call)::set_target_temperature); + parse_num_param_(request, ESPHOME_F("target_temperature_high"), call, &decltype(call)::set_target_temperature_high); + parse_num_param_(request, ESPHOME_F("target_temperature_low"), call, &decltype(call)::set_target_temperature_low); + parse_num_param_(request, ESPHOME_F("target_temperature"), call, &decltype(call)::set_target_temperature); DEFER_ACTION(call, call.perform()); request->send(200); @@ -1725,7 +1724,7 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa return; } - parse_float_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position); + parse_num_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position); DEFER_ACTION(call, call.perform()); request->send(200); @@ -1869,12 +1868,12 @@ void WebServer::handle_water_heater_request(AsyncWebServerRequest *request, cons parse_string_param_(request, ESPHOME_F("mode"), base_call, &water_heater::WaterHeaterCall::set_mode); // Parse temperature parameters - parse_float_param_(request, ESPHOME_F("target_temperature"), base_call, - &water_heater::WaterHeaterCall::set_target_temperature); - parse_float_param_(request, ESPHOME_F("target_temperature_low"), base_call, - &water_heater::WaterHeaterCall::set_target_temperature_low); - parse_float_param_(request, ESPHOME_F("target_temperature_high"), base_call, - &water_heater::WaterHeaterCall::set_target_temperature_high); + parse_num_param_(request, ESPHOME_F("target_temperature"), base_call, + &water_heater::WaterHeaterCall::set_target_temperature); + parse_num_param_(request, ESPHOME_F("target_temperature_low"), base_call, + &water_heater::WaterHeaterCall::set_target_temperature_low); + parse_num_param_(request, ESPHOME_F("target_temperature_high"), base_call, + &water_heater::WaterHeaterCall::set_target_temperature_high); // Parse away mode parameter parse_bool_param_(request, ESPHOME_F("away"), base_call, &water_heater::WaterHeaterCall::set_away); @@ -1978,7 +1977,7 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur auto call = obj->make_call(); // Parse carrier frequency (optional) - if (request->hasArg(ESPHOME_F("carrier_frequency"))) { + { auto value = parse_number(request->arg(ESPHOME_F("carrier_frequency")).c_str()); if (value.has_value()) { call.set_carrier_frequency(*value); @@ -1986,7 +1985,7 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur } // Parse repeat count (optional, defaults to 1) - if (request->hasArg(ESPHOME_F("repeat_count"))) { + { auto value = parse_number(request->arg(ESPHOME_F("repeat_count")).c_str()); if (value.has_value()) { call.set_repeat_count(*value); @@ -1995,17 +1994,12 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur // Parse base64url-encoded raw timings (required) // Base64url is URL-safe: uses A-Za-z0-9-_ (no special characters needing escaping) - if (!request->hasArg(ESPHOME_F("data"))) { - request->send(400, ESPHOME_F("text/plain"), ESPHOME_F("Missing 'data' parameter")); - return; - } - // .c_str() is required for Arduino framework where arg() returns Arduino String instead of std::string std::string encoded = request->arg(ESPHOME_F("data")).c_str(); // NOLINT(readability-redundant-string-cstr) - // Validate base64url is not empty + // Validate base64url is not empty (also catches missing parameter since arg() returns empty string) if (encoded.empty()) { - request->send(400, ESPHOME_F("text/plain"), ESPHOME_F("Empty 'data' parameter")); + request->send(400, ESPHOME_F("text/plain"), ESPHOME_F("Missing or empty 'data' parameter")); return; } diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index c5e4c04f6f..a67957a7f0 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -513,11 +513,9 @@ class WebServer : public Controller, template void parse_light_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(float), float scale = 1.0f) { - if (request->hasArg(param_name)) { - auto value = parse_number(request->arg(param_name).c_str()); - if (value.has_value()) { - (call.*setter)(*value / scale); - } + auto value = parse_number(request->arg(param_name).c_str()); + if (value.has_value()) { + (call.*setter)(*value / scale); } } @@ -525,34 +523,19 @@ class WebServer : public Controller, template void parse_light_param_uint_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(uint32_t), uint32_t scale = 1) { - if (request->hasArg(param_name)) { - auto value = parse_number(request->arg(param_name).c_str()); - if (value.has_value()) { - (call.*setter)(*value * scale); - } + auto value = parse_number(request->arg(param_name).c_str()); + if (value.has_value()) { + (call.*setter)(*value * scale); } } #endif - // Generic helper to parse and apply a float parameter - template - void parse_float_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(float)) { - if (request->hasArg(param_name)) { - auto value = parse_number(request->arg(param_name).c_str()); - if (value.has_value()) { - (call.*setter)(*value); - } - } - } - - // Generic helper to parse and apply an int parameter - template - void parse_int_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(int)) { - if (request->hasArg(param_name)) { - auto value = parse_number(request->arg(param_name).c_str()); - if (value.has_value()) { - (call.*setter)(*value); - } + // Generic helper to parse and apply a numeric parameter + template + void parse_num_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(NumT)) { + auto value = parse_number(request->arg(param_name).c_str()); + if (value.has_value()) { + (call.*setter)(*value); } } @@ -573,8 +556,8 @@ class WebServer : public Controller, // Invalid values are ignored (setter not called) template void parse_bool_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(bool)) { - if (request->hasArg(param_name)) { - const auto ¶m_value = request->arg(param_name); + const auto ¶m_value = request->arg(param_name); + if (!param_value.empty()) { // First check on/off (default), then true/false (custom) auto val = parse_on_off(param_value.c_str()); if (val == PARSE_NONE) { diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index bb83aa9902..1f59835268 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -412,7 +412,7 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name) { return param; } -optional AsyncWebServerRequest::find_query_value_(const char *name) { +optional AsyncWebServerRequest::find_query_value_(const char *name) const { auto val = query_key_value(this->post_query_.c_str(), this->post_query_.size(), name); if (val.has_value()) { return val; diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index f4c92bb8c4..12df0303de 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -186,7 +186,7 @@ class AsyncWebServerRequest { // is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid // duplicate storage. Only successful lookups are cached to prevent cache pollution when // handlers check for optional parameters that don't exist. - optional find_query_value_(const char *name); + optional find_query_value_(const char *name) const; std::vector params_; std::string post_query_; AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}