[water_heater] (3/4) Implement web_server for new water_heater component (#12511)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
Douwe
2026-01-09 15:25:42 -10:00
committed by GitHub
co-authored by pre-commit-ci-lite[bot] J. Nick Koston J. Nick Koston
parent e0ff7fdaa1
commit cea2878b55
4 changed files with 214 additions and 41 deletions
@@ -136,7 +136,7 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont
#ifdef USE_WATER_HEATER
bool ListEntitiesIterator::on_water_heater(water_heater::WaterHeater *obj) {
// Water heater web_server support not yet implemented - this stub acknowledges the entity
this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::water_heater_all_json_generator);
return true;
}
#endif
File diff suppressed because it is too large Load Diff
+47 -5
View File
@@ -35,6 +35,13 @@ extern const size_t ESPHOME_WEBSERVER_JS_INCLUDE_SIZE;
namespace esphome::web_server {
// Type for parameter names that can be stored in flash on ESP8266
#ifdef USE_ESP8266
using ParamNameType = const __FlashStringHelper *;
#else
using ParamNameType = const char *;
#endif
/// Result of matching a URL against an entity
struct EntityMatchResult {
bool matched; ///< True if entity matched the URL
@@ -429,6 +436,16 @@ class WebServer : public Controller,
static std::string alarm_control_panel_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_WATER_HEATER
void on_water_heater_update(water_heater::WaterHeater *obj) override;
/// Handle a water_heater request under '/water_heater/<id>/<mode/set>'.
void handle_water_heater_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string water_heater_state_json_generator(WebServer *web_server, void *source);
static std::string water_heater_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_EVENT
void on_event(event::Event *obj) override;
@@ -472,7 +489,7 @@ class WebServer : public Controller,
#ifdef USE_LIGHT
// Helper to parse and apply a float parameter with optional scaling
template<typename T, typename Ret>
void parse_light_param_(AsyncWebServerRequest *request, const char *param_name, T &call, Ret (T::*setter)(float),
void parse_light_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(float),
float scale = 1.0f) {
if (request->hasParam(param_name)) {
auto value = parse_number<float>(request->getParam(param_name)->value().c_str());
@@ -484,7 +501,7 @@ class WebServer : public Controller,
// Helper to parse and apply a uint32_t parameter with optional scaling
template<typename T, typename Ret>
void parse_light_param_uint_(AsyncWebServerRequest *request, const char *param_name, T &call,
void parse_light_param_uint_(AsyncWebServerRequest *request, ParamNameType param_name, T &call,
Ret (T::*setter)(uint32_t), uint32_t scale = 1) {
if (request->hasParam(param_name)) {
auto value = parse_number<uint32_t>(request->getParam(param_name)->value().c_str());
@@ -497,7 +514,7 @@ class WebServer : public Controller,
// Generic helper to parse and apply a float parameter
template<typename T, typename Ret>
void parse_float_param_(AsyncWebServerRequest *request, const char *param_name, T &call, Ret (T::*setter)(float)) {
void parse_float_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(float)) {
if (request->hasParam(param_name)) {
auto value = parse_number<float>(request->getParam(param_name)->value().c_str());
if (value.has_value()) {
@@ -508,7 +525,7 @@ class WebServer : public Controller,
// Generic helper to parse and apply an int parameter
template<typename T, typename Ret>
void parse_int_param_(AsyncWebServerRequest *request, const char *param_name, T &call, Ret (T::*setter)(int)) {
void parse_int_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(int)) {
if (request->hasParam(param_name)) {
auto value = parse_number<int>(request->getParam(param_name)->value().c_str());
if (value.has_value()) {
@@ -519,7 +536,7 @@ class WebServer : public Controller,
// Generic helper to parse and apply a string parameter
template<typename T, typename Ret>
void parse_string_param_(AsyncWebServerRequest *request, const char *param_name, T &call,
void parse_string_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call,
Ret (T::*setter)(const std::string &)) {
if (request->hasParam(param_name)) {
// .c_str() is required for Arduino framework where value() returns Arduino String instead of std::string
@@ -528,6 +545,28 @@ class WebServer : public Controller,
}
}
// Generic helper to parse and apply a bool parameter
// Accepts: "on", "true", "1" (case-insensitive) as true
// Accepts: "off", "false", "0" (case-insensitive) as false
// Invalid values are ignored (setter not called)
template<typename T, typename Ret>
void parse_bool_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(bool)) {
if (request->hasParam(param_name)) {
auto param_value = request->getParam(param_name)->value();
// First check on/off (default), then true/false (custom)
auto val = parse_on_off(param_value.c_str());
if (val == PARSE_NONE) {
val = parse_on_off(param_value.c_str(), "true", "false");
}
if (val == PARSE_ON || param_value == "1") {
(call.*setter)(true);
} else if (val == PARSE_OFF || param_value == "0") {
(call.*setter)(false);
}
// PARSE_NONE/PARSE_TOGGLE: ignore invalid values
}
}
web_server_base::WebServerBase *base_;
#ifdef USE_ESP32
AsyncEventSource events_{"/events", this};
@@ -606,6 +645,9 @@ class WebServer : public Controller,
#ifdef USE_EVENT
std::string event_json_(event::Event *obj, const std::string &event_type, JsonDetail start_config);
#endif
#ifdef USE_WATER_HEATER
std::string water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config);
#endif
#ifdef USE_UPDATE
std::string update_json_(update::UpdateEntity *obj, JsonDetail start_config);
#endif
@@ -232,6 +232,13 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
}
#endif
#ifdef USE_WATER_HEATER
for (auto *obj : App.get_water_heaters()) {
if (this->include_internal_ || !obj->is_internal())
write_row(stream, obj, "water_heater", "");
}
#endif
stream->print(ESPHOME_F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/\">ESPHome Web API</a> for "
"REST API documentation.</p>"));
#if defined(USE_WEBSERVER_OTA) && !defined(USE_WEBSERVER_OTA_DISABLED)