From 3e11a9d8a5f57a82f702348151ae480bcb6ca50d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 30 Jan 2026 20:20:24 -0600 Subject: [PATCH 1/2] [ci] Block new scanf() usage to prevent ~9.8KB flash bloat --- .../update/esp32_hosted_update.cpp | 2 +- script/ci-custom.py | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp b/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp index ebcdd5f36e..0eac76bf15 100644 --- a/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp +++ b/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp @@ -38,7 +38,7 @@ static const char *const ESP_HOSTED_VERSION_STR = STRINGIFY(ESP_HOSTED_VERSION_M // Returns true if parsing succeeded static bool parse_version(const std::string &version_str, int &major, int &minor, int &patch) { major = minor = patch = 0; - if (sscanf(version_str.c_str(), "%d.%d.%d", &major, &minor, &patch) >= 2) { + if (sscanf(version_str.c_str(), "%d.%d.%d", &major, &minor, &patch) >= 2) { // NOLINT return true; } return false; diff --git a/script/ci-custom.py b/script/ci-custom.py index b146c9867e..58df8d3517 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -756,6 +756,28 @@ def lint_no_sprintf(fname, match): ) +@lint_re_check( + # Match scanf family functions: scanf, sscanf, fscanf, vscanf, vsscanf, vfscanf + # Also match std:: prefixed versions + # [^\w] ensures we match function calls, not substrings + r"[^\w]((?:std::)?v?[fs]?scanf)\s*\(" + CPP_RE_EOL, + include=cpp_include, +) +def lint_no_scanf(fname, match): + func = match.group(1) + return ( + f"{highlight(func + '()')} is not allowed in new ESPHome code. The scanf family " + f"pulls in ~9.8KB of flash on embedded platforms, and ESPHome doesn't otherwise " + f"need this code.\n" + f"Please use alternatives:\n" + f" - {highlight('parse_number(str)')} for parsing integers/floats from strings\n" + f" - {highlight('strtol()/strtof()')} for C-style number parsing with error checking\n" + f" - {highlight('parse_hex()')} for hex string parsing\n" + f" - Manual parsing for simple fixed formats\n" + f"(If strictly necessary, add `// NOLINT` to the end of the line)" + ) + + @lint_content_find_check( "ESP_LOG", include=["*.h", "*.tcc"], From e68b302bba53314ef1d3a9b1a1ee619d9248879d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 30 Jan 2026 20:21:43 -0600 Subject: [PATCH 2/2] [ci] Block new scanf() usage to prevent ~9.8KB flash bloat --- script/ci-custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/ci-custom.py b/script/ci-custom.py index 58df8d3517..b5bec74fa7 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -767,7 +767,7 @@ def lint_no_scanf(fname, match): func = match.group(1) return ( f"{highlight(func + '()')} is not allowed in new ESPHome code. The scanf family " - f"pulls in ~9.8KB of flash on embedded platforms, and ESPHome doesn't otherwise " + f"pulls in ~7KB flash on ESP8266 and ~9KB on ESP32, and ESPHome doesn't otherwise " f"need this code.\n" f"Please use alternatives:\n" f" - {highlight('parse_number(str)')} for parsing integers/floats from strings\n"