esphome / feature-requests

ESPHome Feature Request Tracker
https://esphome.io/
422 stars 27 forks source link

Deep sleep enter : make "until" templatable or add a set_time_until parameter #2532

Open sduponch opened 11 months ago

sduponch commented 11 months ago

Describe the problem you have/What new integration you would like

I use an ESP32-S2 to control the power of a battery powered device using Home Assistant and a Calendar to turn off or turn on the device. To minimize consumption, i want to set the ESP32 in deep sleep mode until the start (or the end) of the next event using the internal RTC clock.

Please describe your use case for this integration and alternatives you've tried:

I tried to set the "until" parameter of deep_sleep.enter action using lambda but this parameter is not templatable. I searched to build a DeepSleepEnterAction using lambda code but i could not find any exemple, i saw interesting function like set_until(hh,mm,ss) but did not managed to use it.

Additional context

I saw another merged pull request for adding "deep sleep duration" as parameter of deep_sleep.enter function, maybe we should do something similar for until and time_id "set_until(time_id, hh, mm, ss)"

ssieb commented 11 months ago

The until: option is not templatable, but why would that even be useful? If you're going to try to calculate an end time, why not just calculate the duration instead?

sduponch commented 11 months ago

To not have to do the calculations, i get the start_time and the end_time directly from the event. In lambda code, i have to parse the dates to timestamp (but i did not find a function for that), then do the diff with now().

I tried something like that, but i prefere to not copy/paste the set_until(...) code in lamdba.

      - lambda: !lambda |-
            // Get only the HH:MM:SS part from "YYYY-MM-DD HH:MM:SS"
            ESP_LOGD("main", "Google calendar start_time %s", id(start_time).state.substr(11,8).c_str());
            ESP_LOGD("main", "Google calendar end_time %s", id(end_time).state.substr(11,8).c_str());
            int hh = 0;
            int mm = 0;
            int ss = 0;
            if(strcmp(id(enabled).state.c_str(), "off") == 0) {
              hh = atoi(id(start_time).state.substr(11,2).c_str());
              mm = atoi(id(start_time).state.substr(14,2).c_str());
              ss = atoi(id(start_time).state.substr(17,2).c_str());
            } else if(strcmp(id(enabled).state.c_str(), "on") == 0) {
              hh = atoi(id(end_time).state.substr(11,2).c_str());
              mm = atoi(id(end_time).state.substr(14,2).c_str());
              ss = atoi(id(end_time).state.substr(17,2).c_str());
            }
            ESP_LOGD("main", "Next wakeup at %d:%d:%d", hh, mm, ss);
            id(deepsleep).set_until(hh,mm,ss);
            //id(deepsleep).set_sleep_duration(....;
            //id(deepsleep).begin_sleep();

if until is templatable this should be something like this :

- deep_sleep.enter:
  id: deepsleep
  time_id: sntp_time
  until: !lambda return id(start_time).state.substr(11,8).c_str());

This is a bit more fancy.

Best regards, Sebastian.

sduponch commented 11 months ago

I managed to do a workaround via lamdba code using strptime for my use case.

      - lambda: !lambda |-
            ESP_LOGD("update_state_from_calendar", "Google calendar is %s", id(enabled).state.c_str());
            ESP_LOGD("main", "Google calendar start_time %s", id(start_time).state.c_str());
            ESP_LOGD("main", "Google calendar end_time %s", id(end_time).state.c_str());
            std::tm next_event_tm = {};

            if(strcmp(id(enabled).state.c_str(), "off") == 0) {
              strptime(id(start_time).state.c_str(), "%Y-%m-%d %H:%M:%S", &next_event_tm);
            } else if(strcmp(id(enabled).state.c_str(), "on") == 0) {
              strptime(id(end_time).state.c_str(), "%Y-%m-%d %H:%M:%S", &next_event_tm);
            }
            long int next = mktime(&next_event_tm) - id(sntp_time).now().timestamp;
            ESP_LOGD("main", "Next event in %ld seconds", next);
            id(deepsleep).set_sleep_duration(next * 1000);
            id(deepsleep).begin_sleep(false);

I do not understand what is the difference between duration sleep and RTC sleep then why RTC sleep require a time_id...

ssieb commented 11 months ago

If your enabled was a binary sensor, it would be easier. If by "RTC sleep" you mean the until: option, it has to calculate the duration from the given time, so it needs a time component. You don't have to specify the id, it will automatically use the configured one.

voska commented 10 months ago

I'd love to see this as well. I have a battery powered light sensor running off an esp32c3 and I want it to sleep all night when there is no lux value to report to extend battery life.

Instead of a simple until: !lambda 'return id(sunrise).state;' I have to manually calculate the time difference between time.now and sunrise and use that in sleep_duration. Annoying and ugly code.

francodutch commented 8 months ago

This would be very useful. I have a standard wake-up time that I'd like to change every now and then without having to recompile the firmware, by reading it from a HA sensor.

ZingPow commented 7 months ago

until: !lambda 'return id(sunrise).state;' would be great for my bumblebee box monitors. They are only active during the day and I'd love to save as much battery as I could.

ssieb commented 7 months ago

Instead of a simple until: !lambda 'return id(sunrise).state;' I have to manually calculate the time difference between time.now and sunrise and use that in sleep_duration. Annoying and ugly code.

That should be a trivial subtract and multiply if you're using the sun component.

ZingPow commented 7 months ago

So I'm stuck here. All I want to do is put an ESP32 device to sleep at sunset and wake it up at sunrise. I would think this is a common use case for solar powered devices.

text_sensor:

time:

deep_sleep: id: big_snooze

sun: id: sun_component latitude: x° longitude: y°

on_sunset: then: