ESPHome time calculations

I’m struggling with some time calculations on an ESP32 in ESPhome. This is for a Lilygo TDisplay S3 device.

I want to display three times: the current time, the time that a significant event happened, and how long ago did that event happen. To do this, I need to track three times, all of which are to be formatted as “HH:MM”:

  • The current time of day
  • A stored time of day: pressing a button should store the current time of day
  • The time difference between the two times (current time is always larger than stored time.)

Getting the current time of day is easy with id(sntp_time).now() and formatting it with strftime("$I:$M").

My problem comes with how to store the time, and how to calculate and format the difference. I kind-of-sort-of have it working, but I think the approach is ugly and there has to be a better way.

To store the time, I tried to use a global of type: time but that throws an unknown type compilation error. I tried several other types (can’t remember them all.) What I’m using for now is type: int for the global, and when it’s time to store the current time, I use id(savedTime) = time.timestamp; from within a button.on_press lambda expression. This stores the current time as a Unix epoch time.

Then, to calculate the time difference, I get the difference in seconds with int seconds = id(ha_time).now().timestamp - id(savedTime);. Then I use integer division and modulo math to get hours and minutes. Finally, I display them using an it.printf(..., "%2.2d:%2.2d", h, m) call. Is there a better way?

In addition, I want HA to be able to read the three times as human readable strings. I’m currently doing this with template text sensors:

  • The current time uses a lambda of return {id(ha_time).now().strftime("%Y-%m-%d %H:%M:%S").c_str()};
  • For the stored time, I’m using a string global which gets set when the time is saved, using strftime() to format the current time as a string. (When stored, the current time is saved as both a string that can be read by HA, and as an integer timestamp that can be used in subsequent calculations.)
  • For the time difference, I’m struggling with how to take the difference (which I have been calculating as seconds) into a clean string that can be returned by the text sensor. I’m using snprintf() into a buffer, but I’m not thrilled with that.

So, I’m looking for advice on the best way to handle this. Basically, store a time so that it can be human readable by HA, and calculate the difference between the times that can be human readable by HA and displayed on the local LCD.

Post your current config.

1 Like

Thank you. I’d be happy to post the code, but it’s big and has lots of extraneous stuff in it that’s not germane to the topic. It will take some time to condense it to a minimum working example. But that may no longer be necessary.

I’ve been doing a bunch more research and experimentation with the goal of setting up a global with a type that can store the result of id(ha_time).now()

But upon more searching, I must’ve finally stumbled upon the right search terms and I found this post: Storing time_t to flash - #4 by hpi

Based on this, I can create a global:

globals:
  - id: savedTime
    type: time_t
    restore_value: True
    initial_value: '0'

I can save the time in that global with this lambda expression:

      id(savedTime) = id(ha_time).now().timestamp;

I can then use that value directly to figure out how many seconds have passed between the current time and the saved time:

      int seconds = id(ha_time).now().timestamp - id(savedTime);

And I can easily format that saved time into a string:

      char str[8];
      strftime(str, sizeof(str), "%I:%M", localtime(&id(savedTime)));
      return str;

This lets me do everything that I want to do, and it’s cleaner and simpler than what I had before.

But I would still be a bit happier if I didn’t have to have that temporary string buffer. So I did more research, and after a lot of struggling and pulling out my hair (caused by trying to reference ESPtime instead of ESPTime) I came up with this:

      return ESPTime::from_epoch_local(id(savedTime)).strftime("%I:%M:%S").c_str();

Is there a better way to handle all of this? I’m sure there is, but it works, and I’m finally satisfied with the way it looks. :sunglasses: