From 4db761ef03f477d645ed7644e5a49a61d879d163 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 Feb 2026 11:20:29 +0100 Subject: [PATCH 1/3] [web_server] Fix uptime display overflow after ~24.8 days using 64-bit chrono --- esphome/components/web_server/web_server.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index d30cb524f4..6268f05b1e 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -15,6 +15,8 @@ #include "StreamString.h" #endif +#include +#include #include #ifdef USE_LIGHT @@ -365,7 +367,17 @@ void WebServer::set_css_include(const char *css_include) { this->css_include_ = void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; } #endif +<<<<<<< Updated upstream std::string WebServer::get_config_json() { +======= +/// Get uptime in milliseconds using std::chrono::steady_clock (64-bit, no rollover) +static int64_t get_uptime_ms() { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) + .count(); +} + +json::SerializationBuffer<> WebServer::get_config_json() { +>>>>>>> Stashed changes json::JsonBuilder builder; JsonObject root = builder.root(); @@ -380,6 +392,7 @@ std::string WebServer::get_config_json() { #endif root[ESPHOME_F("log")] = this->expose_log_; root[ESPHOME_F("lang")] = "en"; + root[ESPHOME_F("uptime")] = get_uptime_ms(); return builder.serialize(); } @@ -403,7 +416,11 @@ void WebServer::setup() { // doesn't need defer functionality - if the queue is full, the client JS knows it's alive because it's clearly // getting a lot of events - this->set_interval(10000, [this]() { this->events_.try_send_nodefer("", "ping", millis(), 30000); }); + this->set_interval(10000, [this]() { + char buf[32]; + buf_append_printf(buf, sizeof(buf), 0, "{\"uptime\":%" PRId64 "}", get_uptime_ms()); + this->events_.try_send_nodefer(buf, "ping", millis(), 30000); + }); } void WebServer::loop() { this->events_.loop(); } From 14b42d5997079f93763d3f491ec7ffcfade5a26b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 Feb 2026 11:25:45 +0100 Subject: [PATCH 2/3] fix conflict --- esphome/components/web_server/web_server.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 6268f05b1e..37f9021870 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -367,9 +367,6 @@ void WebServer::set_css_include(const char *css_include) { this->css_include_ = void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; } #endif -<<<<<<< Updated upstream -std::string WebServer::get_config_json() { -======= /// Get uptime in milliseconds using std::chrono::steady_clock (64-bit, no rollover) static int64_t get_uptime_ms() { return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) @@ -377,7 +374,6 @@ static int64_t get_uptime_ms() { } json::SerializationBuffer<> WebServer::get_config_json() { ->>>>>>> Stashed changes json::JsonBuilder builder; JsonObject root = builder.root(); From badf09164e0a45b0e03d6e3ae9e8b0ac78535e7d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Feb 2026 22:44:12 -0600 Subject: [PATCH 3/3] [web_server] Reuse scheduler millis_64 for uptime instead of std::chrono Replace the std::chrono::steady_clock approach with the scheduler's existing millis_64() rollover tracking. This avoids pulling in gettimeofday_r, system_clock, and the ArduinoJson int64_t serializer template, saving ~270 bytes of flash and 8 bytes of RAM on ESP8266. Send uptime as uint32_t seconds (good for ~136 years) instead of int64_t milliseconds. --- esphome/components/web_server/web_server.cpp | 13 +++---------- esphome/core/scheduler.cpp | 2 ++ esphome/core/scheduler.h | 3 +++ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 94695eb60c..d8a6eb1f43 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -15,8 +15,6 @@ #include "StreamString.h" #endif -#include -#include #include #ifdef USE_LIGHT @@ -372,12 +370,6 @@ void WebServer::set_css_include(const char *css_include) { this->css_include_ = void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; } #endif -/// Get uptime in milliseconds using std::chrono::steady_clock (64-bit, no rollover) -static int64_t get_uptime_ms() { - return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) - .count(); -} - json::SerializationBuffer<> WebServer::get_config_json() { json::JsonBuilder builder; JsonObject root = builder.root(); @@ -393,7 +385,7 @@ json::SerializationBuffer<> WebServer::get_config_json() { #endif root[ESPHOME_F("log")] = this->expose_log_; root[ESPHOME_F("lang")] = "en"; - root[ESPHOME_F("uptime")] = get_uptime_ms(); + root[ESPHOME_F("uptime")] = static_cast(App.scheduler.millis_64() / 1000); return builder.serialize(); } @@ -422,7 +414,8 @@ void WebServer::setup() { // getting a lot of events this->set_interval(10000, [this]() { char buf[32]; - buf_append_printf(buf, sizeof(buf), 0, "{\"uptime\":%" PRId64 "}", get_uptime_ms()); + auto uptime = static_cast(App.scheduler.millis_64() / 1000); + buf_append_printf(buf, sizeof(buf), 0, "{\"uptime\":%u}", uptime); this->events_.try_send_nodefer(buf, "ping", millis(), 30000); }); } diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 3294f689e8..e82efcc520 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -675,6 +675,8 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, NameType name_type return total_cancelled > 0; } +uint64_t Scheduler::millis_64() { return this->millis_64_(millis()); } + uint64_t Scheduler::millis_64_(uint32_t now) { // THREAD SAFETY NOTE: // This function has three implementations, based on the precompiler flags diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 394178a831..afe11aaca6 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -116,6 +116,9 @@ class Scheduler { ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") bool cancel_retry(Component *component, uint32_t id); + /// Get 64-bit millisecond timestamp (handles 32-bit millis() rollover) + uint64_t millis_64(); + // Calculate when the next scheduled item should run // @param now Fresh timestamp from millis() - must not be stale/cached // Returns the time in milliseconds until the next scheduled item, or nullopt if no items