Uptime sensor in hours or days?

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

9 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

4 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:

4 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.

Hm, that was 3 years ago, and I have completely changed my approach. That code is probably still going to work fine - I haven’t heard about deprecating the old template format.

What I am doing nowadays is to have a time entity on the ESP device, then have a text_sensor that exposes that value, and then set the device class to timestamp inside Home Assistant. The major advantage of this approach is that the entity only updates once (as opposed to every 60 seconds) and thus reduces the load on the database, and it uses the built-in way to display a human-readable uptime like “2 days ago” or “Last month”.

text_sensor:
  - platform: template
    name: "${display_devicename} Last Restart Time"
    id: last_restart_time
    icon: mdi:clock
    entity_category: diagnostic
#    device_class: timestamp

time:
  - platform: homeassistant
    id: homeassistant_time
    on_time_sync:
      then:
        # Update last restart time, but only once.
        - if:
            condition:
              lambda: 'return id(last_restart_time).state == "";'
            then:
              - text_sensor.template.publish:
                  id: last_restart_time
                  state: !lambda 'return id(homeassistant_time).utcnow().strftime("%Y-%m-%dT%H:%M:%S.000000+00:00");'

I’m using this code (original code from Frenck):

time:
  - platform: homeassistant
    id: current_time
    on_time_sync:
      - component.update: uptime_timestamp

sensor:
  - platform: uptime
    id: uptime_sec

  - platform: template
    id: uptime_timestamp
    name: "$devicename Uptime"
    device_class: timestamp
    entity_category: diagnostic
    accuracy_decimals: 0
    update_interval: never
    lambda: |-
      static float timestamp = (
        id(current_time).utcnow().timestamp - id(uptime_sec).state
      );
      return timestamp;
1 Like

I use the same, but I’ve set the first sensor to internal only, so it won’t end up in HA.

It’s internal by default if it has no name:

ah, didn’t know that, thanks :slight_smile:

modified it to comply with new guidelines

template:
  - sensor:
      - name: "Huawei Modem uptime"
        state: >
          {% set uptime = states.sensor.huawei_igd_uptime.state | int %}
          {% set days = (uptime / 86400) | int %}
          {%- if days > 0 -%}
            {{ days }} days, {{ (uptime - (days * 86400)) | int | timestamp_custom('%H:%M:%S', false) }}
          {%- else -%}
            {{ uptime | int | timestamp_custom('%H:%M:%S', false) }}
          {%- endif -%}

Edit: removed unit, bcz it is self explanatory.
image
image

device_class: duration

can be used but what difference it makes is still not known in uptime.

Great! it works.
It gives warning in compilation but it works. thank!