From 93aecdd4350cdcd18cada66de001d0423a9ce0f6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 8 Oct 2026 17:58:37 -1000 Subject: [PATCH] Deprecate Sprinkler::valve_name() and guard ProgmemStringRef::write_to valve_name() returned a flash pointer on ESP8266; it now follows get_name(): static_assert there, deprecated elsewhere, with valve_log_name() for logs. --- esphome/components/sprinkler/sprinkler.cpp | 14 +++++++-- esphome/components/sprinkler/sprinkler.h | 11 +++++++ esphome/core/string_ref.h | 2 ++ tests/components/core/test_string_ref.cpp | 36 ++++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index c00240d9d80..d3c272c043d 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -870,9 +870,18 @@ void Sprinkler::reset_resume() { this->resume_duration_.reset(); } +#ifndef USE_ESP8266 const char *Sprinkler::valve_name(const size_t valve_number) { if (this->is_a_valid_valve(valve_number)) { - return LOG_STR_ARG(this->valve_[valve_number].controller_switch->get_log_name()); + return this->valve_[valve_number].controller_switch->get_name_progmem().progmem_ptr(); // RAM off ESP8266 + } + return nullptr; +} +#endif + +const LogString *Sprinkler::valve_log_name(const size_t valve_number) { + if (this->is_a_valid_valve(valve_number)) { + return this->valve_[valve_number].controller_switch->get_log_name(); } return nullptr; } @@ -1588,7 +1597,8 @@ void Sprinkler::dump_config() { " Valve %zu:\n" " Name: %s\n" " Run Duration: %" PRIu32 " seconds", - valve_number, this->valve_name(valve_number), this->valve_run_duration(valve_number)); + valve_number, LOG_STR_ARG(this->valve_log_name(valve_number)), + this->valve_run_duration(valve_number)); } if (!this->pump_.empty()) { ESP_LOGCONFIG(TAG, " Total number of pumps: %zu", this->pump_.size()); diff --git a/esphome/components/sprinkler/sprinkler.h b/esphome/components/sprinkler/sprinkler.h index 2499a0a5915..d3708a436f3 100644 --- a/esphome/components/sprinkler/sprinkler.h +++ b/esphome/components/sprinkler/sprinkler.h @@ -335,8 +335,19 @@ class Sprinkler final : public Component { /// resets resume state void reset_resume(); +#ifdef USE_ESP8266 + template const char *valve_name(size_t /*valve_number*/) { + static_assert(sizeof(T) == 0, "valve_name() unavailable on ESP8266 (name is in flash). Use valve_log_name() " + "for logging, or valve_switch()->get_name_to() for a copy."); + return nullptr; + } +#else /// returns a pointer to a valve's name string object; returns nullptr if valve_number is invalid + ESPDEPRECATED("Use valve_log_name() or valve_switch()->get_name_to() instead. Removed in 2027.5.0", "2026.11.0") const char *valve_name(size_t valve_number); +#endif + /// returns a valve's name for a "%s" log argument; returns nullptr if valve_number is invalid + const LogString *valve_log_name(size_t valve_number); /// returns what invoked the valve that is currently active, if any. check with 'has_value()' optional active_valve_request_is_from(); diff --git a/esphome/core/string_ref.h b/esphome/core/string_ref.h index 40588b04e58..69c33cfbf6c 100644 --- a/esphome/core/string_ref.h +++ b/esphome/core/string_ref.h @@ -237,6 +237,8 @@ class ProgmemStringRef { /// Copy into buf (null terminated, truncated to fit), returns the length copied. size_t write_to(char *buf, size_t buf_size) const { + if (buf_size == 0) + return 0; size_t len = std::min(this->len_, buf_size - 1); #ifdef USE_ESP8266 memcpy_P(buf, this->base_, len); diff --git a/tests/components/core/test_string_ref.cpp b/tests/components/core/test_string_ref.cpp index f66800dd178..baaf6fe12f8 100644 --- a/tests/components/core/test_string_ref.cpp +++ b/tests/components/core/test_string_ref.cpp @@ -161,4 +161,40 @@ TEST(StringRefNullEmpty, NumericConversionsReturnZero) { EXPECT_EQ(stof(null_empty), 0.0f); } +TEST(ProgmemStringRef, WriteToCopiesAndTerminates) { + ProgmemStringRef ref("Kitchen", 7); + char buf[16]; + EXPECT_EQ(ref.write_to(buf, sizeof(buf)), 7u); + EXPECT_STREQ(buf, "Kitchen"); +} + +TEST(ProgmemStringRef, WriteToTruncatesToFit) { + ProgmemStringRef ref("Kitchen", 7); + char buf[4]; + EXPECT_EQ(ref.write_to(buf, sizeof(buf)), 3u); + EXPECT_STREQ(buf, "Kit"); +} + +TEST(ProgmemStringRef, WriteToZeroSizeWritesNothing) { + ProgmemStringRef ref("Kitchen", 7); + char buf[1] = {'x'}; + EXPECT_EQ(ref.write_to(buf, 0), 0u); + EXPECT_EQ(buf[0], 'x'); +} + +TEST(ProgmemStringRef, EqualsComparesLengthAndContent) { + ProgmemStringRef ref("Kitchen", 7); + EXPECT_TRUE(ref.equals(StringRef("Kitchen"))); + EXPECT_FALSE(ref.equals(StringRef("Kitche"))); + EXPECT_FALSE(ref.equals(StringRef("Kitchens"))); + EXPECT_FALSE(ref.equals(StringRef("Bedroom"))); +} + +TEST(ProgmemStringRef, DefaultIsEmpty) { + ProgmemStringRef ref; + EXPECT_TRUE(ref.empty()); + EXPECT_EQ(ref.size(), 0u); + EXPECT_TRUE(ref.equals(StringRef(""))); +} + } // namespace esphome::core::testing