From 0f4e274e522bb403b2e73fb535e0743c4129e2c4 Mon Sep 17 00:00:00 2001
From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Date: Tue, 21 Jan 2025 13:43:50 +1100
Subject: [PATCH] [uptime] Cosmetic improvements for uptime text_sensor (#8101)

---
 .../components/uptime/text_sensor/__init__.py |  2 +-
 .../uptime/text_sensor/uptime_text_sensor.cpp | 59 ++++++++++++-------
 .../uptime/text_sensor/uptime_text_sensor.h   |  4 +-
 3 files changed, 41 insertions(+), 24 deletions(-)

diff --git a/esphome/components/uptime/text_sensor/__init__.py b/esphome/components/uptime/text_sensor/__init__.py
index 996d983e71..e4a7ac6517 100644
--- a/esphome/components/uptime/text_sensor/__init__.py
+++ b/esphome/components/uptime/text_sensor/__init__.py
@@ -11,7 +11,7 @@ CONFIG_SCHEMA = text_sensor.text_sensor_schema(
     UptimeTextSensor,
     icon=ICON_TIMER,
     entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
-).extend(cv.polling_component_schema("60s"))
+).extend(cv.polling_component_schema("30s"))
 
 
 async def to_code(config):
diff --git a/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp b/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp
index 0fa5e199f3..409af6e4ff 100644
--- a/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp
+++ b/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp
@@ -9,34 +9,51 @@ namespace uptime {
 
 static const char *const TAG = "uptime.sensor";
 
-void UptimeTextSensor::setup() { this->last_ms_ = millis(); }
+void UptimeTextSensor::setup() {
+  this->last_ms_ = millis();
+  if (this->last_ms_ < 60 * 1000)
+    this->last_ms_ = 0;
+  this->update();
+}
 
 void UptimeTextSensor::update() {
-  const auto now = millis();
+  auto now = millis();
   // get whole seconds since last update. Note that even if the millis count has overflowed between updates,
   // the difference will still be correct due to the way twos-complement arithmetic works.
-  const uint32_t delta = (now - this->last_ms_) / 1000;
-  if (delta == 0)
-    return;
-  // set last_ms_ to the last second boundary
-  this->last_ms_ = now - (now % 1000);
+  uint32_t delta = now - this->last_ms_;
+  this->last_ms_ = now - delta % 1000;  // save remainder for next update
+  delta /= 1000;
   this->uptime_ += delta;
   auto uptime = this->uptime_;
-  unsigned days = uptime / (24 * 3600);
-  unsigned seconds = uptime % (24 * 3600);
-  unsigned hours = seconds / 3600;
-  seconds %= 3600;
-  unsigned minutes = seconds / 60;
-  seconds %= 60;
-  if (days != 0) {
-    this->publish_state(str_sprintf("%dd%dh%dm%ds", days, hours, minutes, seconds));
-  } else if (hours != 0) {
-    this->publish_state(str_sprintf("%dh%dm%ds", hours, minutes, seconds));
-  } else if (minutes != 0) {
-    this->publish_state(str_sprintf("%dm%ds", minutes, seconds));
-  } else {
-    this->publish_state(str_sprintf("%ds", seconds));
+  unsigned interval = this->get_update_interval() / 1000;
+  std::string buffer{};
+  // display from the largest unit that corresponds to the update interval, drop larger units that are zero.
+  while (true) {  // enable use of break for early exit
+    unsigned remainder = uptime % 60;
+    uptime /= 60;
+    if (interval < 30) {
+      buffer.insert(0, str_sprintf("%us", remainder));
+      if (uptime == 0)
+        break;
+    }
+    remainder = uptime % 60;
+    uptime /= 60;
+    if (interval < 1800) {
+      buffer.insert(0, str_sprintf("%um", remainder));
+      if (uptime == 0)
+        break;
+    }
+    remainder = uptime % 24;
+    uptime /= 24;
+    if (interval < 12 * 3600) {
+      buffer.insert(0, str_sprintf("%uh", remainder));
+      if (uptime == 0)
+        break;
+    }
+    buffer.insert(0, str_sprintf("%ud", (unsigned) uptime));
+    break;
   }
+  this->publish_state(buffer);
 }
 
 float UptimeTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
diff --git a/esphome/components/uptime/text_sensor/uptime_text_sensor.h b/esphome/components/uptime/text_sensor/uptime_text_sensor.h
index 4baf1039b6..5719ef38a2 100644
--- a/esphome/components/uptime/text_sensor/uptime_text_sensor.h
+++ b/esphome/components/uptime/text_sensor/uptime_text_sensor.h
@@ -17,8 +17,8 @@ class UptimeTextSensor : public text_sensor::TextSensor, public PollingComponent
   float get_setup_priority() const override;
 
  protected:
-  uint64_t uptime_{0};
-  uint64_t last_ms_{0};
+  uint32_t uptime_{0};  // uptime in seconds, will overflow after 136 years
+  uint32_t last_ms_{0};
 };
 
 }  // namespace uptime