Uptime sensor in hours or days?

To be sure to understand well the problem, you want to report multiple uptime from same physical device (which doesn’t have much sense) or from differents ESPs ? If from different ESPs they’ll show up as different variables for sure in HA so not sure what’s your problem there :confused:

@vincen @Klagio

Thanks for this! I’m much happier having my ESP do the work instead of making more sensors in HA. I also learned I can control the number of decimal places:

  - platform: uptime
    name: "Sensor Uptime"
    filters:
      - lambda: return x / 3600.0;
    unit_of_measurement: "hours"
    accuracy_decimals: 2
3 Likes

yes I love esphome because so many things can be done without touching configuration of HASS.

My next goal is to put most of the automation inside ESPhome too.

Is there a reason why you put 3600.0 and not 3600?

No, actually at first I wondered if putting 3600.0 would make it add the decimal place, but then I figured out it didn’t make any difference - I needed the accuracy_decimals statement. But I forgot to change it back to just 3600 :blush:

I’m learning new things every day! :grinning:

Me tto, these are also some nice additions

switch:
  - platform: restart
    name: "power plug restart"

binary_sensor:
  - platform: status
    name: "power plug status"

sensor:
  - platform: wifi_signal
    name: "POW WiFi Signal"
    update_interval: 60s
2 Likes

Like the OP here, I wanted the Uptime Sensor in ESPHome to do the formatting and not have to have Home Assistant do the work. So, I offer this code snippet to @Klagio (and everyone else).

esphome code:

sensor:
  - platform: uptime
    name: "Uptime"
    id: uptime_s
    update_interval: 15s

text_sensor:
  - platform: template
    name: "Uptime (formatted)"
    lambda: |-
      uint32_t dur = id(uptime_s).state;
      int dys = 0;
      int hrs = 0;
      int mnts = 0;
      if (dur > 86399) {
        dys = trunc(dur / 86400);
        dur = dur - (dys * 86400);
      }
      if (dur > 3599) {
        hrs = trunc(dur / 3600);
        dur = dur - (hrs * 3600);
      }
      if (dur > 59) {
        mnts = trunc(dur / 60);
        dur = dur - (mnts * 60);
      }
      char buffer[17];
      sprintf(buffer, "%ud %02uh %02um %02us", dys, hrs, mnts, dur);
      return {buffer};
    icon: mdi:clock-start
    update_interval: 15s

16 Likes

Any idea why I am getting below error?

src\main.cpp:562:21: error: could not convert ‘{buffer}’ from ‘’ to ‘esphome::optional’
return {buffer};
^
src\main.cpp:563:3: warning: control reaches end of non-void function [-Wreturn-type]
});
^
*** [.pioenvs\bw_shp6_powerplug_02\src\main.cpp.o] Error 1

Fantastic, works great

hi all,

i was just looking thorough to find a method to do just this and after reading the above worked out that i can do this …

Copy to clipboard

 - platform: uptime
    name: "Emergency Light uptime"
      filters:
        - lambda: return (x/60)/60;
      unit_of_measurement: "Hrs"

to get a readout in hours… eg… 1 hrs. or 1.5hrs

hope it helps

Found myself. I put everything under sensor and not text_sensor:(

That would do it. :grinning: I’m glad you figured it out.

One more snippet for the crowd:

sensor:
  - platform: uptime
    name: "Uptime"
    id: uptime_sec

  - platform: template
    name: "Uptime Days"
    lambda: |-
      return (id(uptime_sec).state/60)/60/24;
    icon: mdi:clock-start
    unit_of_measurement: days

I wanted to have the uptime still in seconds and have another one breaking it down in days

3 Likes

And…here is another version to show a more human readable uptime in days, hours, minutes, seconds:

sensor:
  - platform: uptime
    id: uptime_sec

text_sensor:
  - platform: template
    name: ${upper_devicename} Uptime
    lambda: |-
      int seconds = (id(uptime_sec).state);
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600); 
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
      return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
    icon: mdi:clock-start
    update_interval: 113s

Result:
uptime

10 Likes

Great stuff guys! Here’s a version I did that only shows variables if they are positive. (i.e. doesn’t state “days” until it has been up for at least 1 day.)

sensor:
  - platform: uptime
    id: uptime_seconds

text_sensor:
  - platform: template
    name: ${friendly_name} - Uptime
    update_interval: 42s
    icon: mdi:clock-start
    lambda: |-
      int seconds = (id(uptime_seconds).state);
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
      if ( days ) {
        return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( hours ) {
        return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( minutes ) {
        return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else {
        return { (String(seconds) +"s").c_str() };
      }

-J

5 Likes

This works like a charm, thanks!

Guys, I’m really liking the human readable uptime strings but have you noticed that just after you program the thing it gives a value that is MASSIVE? For example:
image

Thanks for sharing this @JayElDubya, I used it and added the “Starting up” to take care of the huge negative number right after flashing.

    lambda: |-
      int seconds = (id(${node_name}_uptime_raw).state);
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
      if ( days > 3650 ) {
        return { "Starting up" };
      } else if ( days ) {
        return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( hours ) {
        return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( minutes ) {
        return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else {
        return { (String(seconds) +"s").c_str() };
      }
    icon: mdi:clock-start

I also determined that the huge negative number was causing a boot loop on the ESP32 so now I have used this code to reflash all my ESPHome devices so they come up with more readable and accurate values. :slight_smile:

5 Likes

nice find. Will update my nodes.I have had random reboots on several devices that eventually sort themselves out. I wonder if there’s some variation as to what order things are initialized upon boot. Hopefully this stops the overflow. For what it’s worth, I’ve noticed high values on HA sensor values as well before it actually connects to HA and refreshes the value. Is there a way, maybe with a clever filter, to have an initial value (like 0) for a sensor to have before the data comes in?

-Josh

I’m not super found of limits (although I highly doubt my nodes will reach close to 10 years uptime).

This works even better:

text_sensor:
  - platform: template
    name: ${friendly_name} Uptime
    id: ${node_name}_uptime
    icon: mdi:clock-start
sensor:
  - platform: uptime
    name: "${friendly_name} Uptime Sensor"
    id: ${node_name}_uptime_raw
    update_interval: 60s
    on_raw_value:
      then:
        - logger.log:
            format: "Raw Value of Uptime sensor: %f"
            args: ['id(${node_name}_uptime_raw).raw_state']
            level: INFO
        - text_sensor.template.publish:
            id: ${node_name}_uptime
            state: !lambda |-
              int seconds = round(id(${node_name}_uptime_raw).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              if ( days ) {
                return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
              } else if ( hours ) {
                return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
              } else if ( minutes ) {
                return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
              } else {
                return { (String(seconds) +"s").c_str() };
              }

Edit: I’ve added this as an example to the esphome doc: https://esphome.io/components/sensor/uptime.html#human-readable-sensor

3 Likes

Is this good according to the new template style too? Working fine on my end, but still want to check.