ESPHome - HA Timer Helper on Display

Is there a way to display a timer helper from HA on I2C display in esphome. tried it as sensor with the internal flag, but no lock.

thanks!

HI,

Use a text_sensor:

text_sensor:
  - platform: homeassistant
    entity_id: input_datetime.<your_entity>
    id: my_helper

and in the display component:

display:
  - platform: ...
    model: ...
    id: my_oled
    lambda: |-
      it.printf(0, 0, id(my_font), "helper: %s", id(my_helper).state.c_str());

thanks for the hint, but I do have a timer in HA, not a datetime input helper :slight_smile:

Second attempt :grinning:

text_sensor:
  - platform: homeassistant
    id: my_helper
    entity_id: timer.timer_test

  - platform: homeassistant
    id: my_helper_end
    entity_id: timer.timer_test
    attribute: finishes_at

This gives you the timer status and the date/time the timer will finish when the timer is active. Now you can calculate the remaining time between the current time (see time platform) and the end time of the timer and the result you can display with for instance and update_interval of 1s

thanks - works like a charm :slight_smile: I always tried it with sensors instead of textsensors…

a) Do you have a solution for converting the id (my_helper_end) with is like “2022-02-11T08:18:24+00:00” to a “HH:MM” format? because now it is string and not a timestamp… ideally this will be the ETA, when the timer finishes.

b) the id(my_helper) is either “idle” or “active”: how to get the remaining time?

@jsuanet thanks again!

Here is some code which shows the state of the timer and the remaining hours and minutes when the timer is active. It is possible to show also the remaining seconds, however there is a slight delay between the timer in HA and the information available within the ESP device.

time:
  - platform: homeassistant
    id: ha_time

text_sensor:
  - platform: homeassistant
    id: timer_status
    entity_id: timer.timer_test
  - platform: homeassistant
    id: timer_finish
    entity_id: timer.timer_test
    attribute: finishes_at

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    update_interval: 5s
    id: oled
    lambda: |-
      it.printf(0, 0, id(my_font), "Timer: %s", id(timer_status).state.c_str());

      if (id(timer_status).state == "active") {
        struct tm tm_finish{}, tm_ha_time{};
        time_t t_helper;
        double remain;
        int remain_h, remain_m, remain_s;
        
        strptime(id(timer_finish).state.c_str(), "%Y-%m-%dT%T", &tm_finish); // Convert string to time struct
        t_helper = mktime(&tm_finish) + 3600;  // Convert to time_t and add 1 hour (correct for your timezone)

        tm_ha_time = id(ha_time).now().to_c_tm(); // Get current time in a tm struct
        
        remain = difftime(t_helper, mktime(&tm_ha_time)); // Calculate difference in seconds
        
        remain_h = (int) remain/3600;
        remain_m = (int) (remain - 3600*remain_h)/60;
        remain_s = (int) remain - 3600*remain_h - 60*remain_m; 

        it.printf(40, 30, id(my_font), "%02d:%02d", remain_h, remain_m);
        // it.printf(0, 30, id(my_font), "%02d:%02d:%02d", remain_h, remain_m, remain_s);        

      } else {
        it.printf(0, 30, id(my_font), " "); // clear second line
      } 
6 Likes

big thanks!

2 Likes

works like a charm … but I want to display also the finishing time …

the only way that works is:

        it.printf(63, 42, id(font_t12), TextAlign::BASELINE_RIGHT, "%02d:%02d", tm_finish.tm_hour+1, tm_finish.tm_min);

is there a better way? eg with the function “strftime”`?

is there a way to get the corret timezone from HA? with the function localtime()?

maybe you can give me some feedback on this topic

big thanks again!

Yes it would be nice if the finishes_at already contains the right end_time. So I created a template sensor in HA which holds the local end timestamp and is updated whenever the finishes_at is updated.

platform: template
sensors:
  timer_test_endtime:
    value_template: '{{ as_timestamp(state_attr("timer.timer_test", "finishes_at")) | timestamp_local }}'

If you use the !include_dir_list option for sensors in your HA configuration.yaml, then you can put this in a yaml-file in your HA sensor directory.
Otherwise you have to add this to your sensor part in the configuration.yaml and add some - and “tabs” to format the yaml correct :grinning:

This HA template sensor is exposed to ESPHome and used in the lambda to calculate the remaining time and to display the end time.

Here is the changed part of the ESPHome yaml

text_sensor:
  - platform: homeassistant
    id: timer_status
    entity_id: timer.timer_test
  # - platform: homeassistant
  #   id: timer_finish
  #   entity_id: timer.timer_test
  #   attribute: finishes_at
  - platform: homeassistant
    id: timer_finish
    entity_id: sensor.timer_test_endtime

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    update_interval: 5s
    id: oled
    lambda: |-
      it.printf(0, 0, id(my_font), "Timer: %s", id(timer_status).state.c_str());

      if (id(timer_status).state == "active") {
        struct tm tm_finish{}, tm_ha_time{};
      
        time_t t_helper;
        double remain;
        int remain_h, remain_m, remain_s;

        strptime(id(timer_finish).state.c_str(), "%Y-%m-%dT%T", &tm_finish); // Convert string to time struct
        t_helper = mktime(&tm_finish);  // Convert to time_t

        tm_ha_time = id(ha_time).now().to_c_tm(); // Get current time in a tm struct
        remain = difftime(t_helper, mktime(&tm_ha_time)); // Calculate difference in seconds
        
        remain_h = (int) remain/3600;
        remain_m = (int) (remain - 3600*remain_h)/60;
        remain_s = (int) remain - 3600*remain_h - 60*remain_m; 

        it.printf(40, 20, id(my_font), "%02d:%02d", remain_h, remain_m);
        //it.printf(20, 20, id(my_font), "%02d:%02d:%02d", remain_h, remain_m, remain_s);        
        it.printf(0, 40, id(my_font), "End: %02d:%02d", tm_finish.tm_hour, tm_finish.tm_min);
      } else {
        it.printf(0, 20, id(my_font), " "); // clear second line
        it.printf(0, 40, id(my_font), " "); // clear third line
      }
2 Likes

thanks - but I will stick to my solution

it.printf(63, 42, id(font_t12), TextAlign::BASELINE_RIGHT, "%02d:%02d", tm_finish.tm_hour+1, tm_finish.tm_min);

this is an esphome only solution … hopefully the “tm_hour+1” will also work when we switch the DST - but we’ll see.

as a summery I can say, that calculating with time is not that simple especially in esphome. eg the strftime function does not work as expected when used inside a lambda with local “struct tm” variable. only works with an esphome-time ID.

again - big thanks for your help! highly appreciated!

Just a note of thanks to @jsuanet as I came across this thread while looking for display code for a timer helper. Works perfectly with a couple of modifications.

I’m grateful, thank you.

Is there a special trick to getting this to recognize the active state of the timer.timer_test entity?

I definitely have a running timer named timer_test in HA but it never outputs anything to the display, instead it goes straight to the else statement and prints a blank space.

EDIT: Never mind, I’m a dummy, I completely forgot to add this device in HA, it was only in ESPHome.

Pefect!
I am using this for my teatimer for a while now and love it a lot.

Although over time I came across some strange “slippage” of time.

It seems to be connected to the uptime of the actual device.
It adds a second to the displayed time for around every half an hour of uptime.

So e.g. one hour after reboot the timer starts at 2:02 on the display (max7219) (while HA shows 2:00) and ends at 0:02 (HA: 0:00), so it does not affect the actual runtime just the display.

weird, right?
internal clock problems?
display timing?
any ideas?
I am stumped…

thanks for the time

struct tm tm_finish{}, tm_ha_time{}, tm_duration;
          time_t t_helper,bar_helper;
          double remain;
          int remain_h, remain_m, remain_s, remain_all;

          strptime(id(tea_done).state.c_str(), "%Y-%m-%dT%T", &tm_finish);  // Convert string to time struct .minus 2 seconds for esphome ha delay??
          t_helper = mktime(&tm_finish) + 3600;                             // Convert to time_t and add 1 hour (correct for your timezone)
          tm_ha_time = id(esptime).now().to_c_tm();                         // Get current time in a tm struct
          remain = difftime(t_helper, mktime(&tm_ha_time));                 // Calculate difference in seconds
          ESP_LOGD("time debug", "All remaining seconds: %i", int(remain));
          remain_h = (int) remain/3600;
          remain_m = (int) (remain - 3600*remain_h)/60;
          remain_s = (int) remain - 3600*remain_h - 60*remain_m; 
          ESP_LOGD("time debug", "Remaining: %i:%i:%i", int(remain_h),int(remain_m),int(remain_s));
          it.printf(0, 0, font, "t%2d:%02d", remain_m, remain_s );

I tried to get this metod ob my nextion Display. The usecase is Cooking Timer, which should show the Seconds. I didnt found an other solution.
I made Template Sensor in esphome with the Code below. For testing return only the hours.
By installing i get the Error, that it could Not be convert remain, remain_h and so on from e.g. int to ‘esphome::optional<Std::__cxx11::basic_string >’
Is there a solution? I am Amateur in esphome…