[SOLVED] Relay state based on SNTP time ESP currently booted on.(Local Power Outages!)

I’m trying to do this: ESPHome boots and connect sntp.

if current time falls under a time-range relay1.turn_on, else off
07:30 to 16:30 relay1 on,
17:30 to 05:30 relay1 on,
else off

The problem is there are frequent power outages of long periods.
If the power is out just before a relay trigger, it does not trigger when power is back.

i could not get the time syntax correct as i cannot code.

time:
  - platform: sntp
    id: sntp_time
   on_time:
      then:
        - lambda: |-
          	if ((id(sntp_time).now() >= "07:30:00") & (id(sntp_time).now() =< "16:30:00")) {
          		id(relay1).turn_on();
          	}
          	else if ((id(sntp_time).now() >= "17:30:00") & (id(sntp_time).now() =< "23:59:59")) {
          		auto call = id(relay1).turn_on();
          	}	
          	else if ((id(sntp_time).now() >= "00:00:00") & (id(sntp_time).now() < "05::30:00")) {
          		auto call = id(relay1).turn_on();
            } else {
              //other times
              id(relay1).turn_off();
            }

i found a similar thread 2 days before but still could not transform the code.

Two ideas:

  1. you can try on_time_sync instead of on_time
  2. you might have to add a timezone setting, depending on your location
time:
  - platform: sntp
    timezone: Europe/Amsterdam
    id: sntp_time
    on_time_sync:
      then:
        - lambda: |-

With on_time_sync the automation is run after boot just after the time has been successfully synced, and then every 15 minutes.
When you have to add a timezone you have to be careful with the syntax: there currently is a bug in the ESPHome Time integration with mishandling many of the valid timezone values. See: Time format help.

Thank you for your reply thusassistint Ed
There no issue with time zones. I meant time syntax in lambda.

what happens is "If the power is out just before a relay trigger, when the power comes back on, relay1 is in the wrong state, it does not trigger now that time is past.

Hmm… so just to be sure: when there is no power outage your code always works as expected, but when there is a power outage and the power comes back on at let’s say at 07:31 local time then your code does not work?

Thats absolutly right. i have simplified with 2 time ranges, in practise there are many time ranges with different ESPs.
ESP excecute the task “on time”. but when the time is passed ESP will not do anything if it just woke up.

Below code works assuming no power outages but if power goes from 07:20 to 7:40 ( trigger time 07:30) relay does not come on as just the power is back. Result: its still dark after power is back, manual turn on needed.
https://www.codepile.net/pile/3BD8OaB7

time:
  - platform: sntp
    id: sntp_time
    on_time:
      - seconds: 0
        minutes: 30
        hours: 7
        then:
          - switch.turn_on: relay1

I have asked on discord, they give me this code.

int now = parse_number<int>(id(sntp_time).now().strftime("HHMM")).value();
bool on = (now >= 730 && now <= 1630) || now >= 1730 || now <= 530;
id(relay1).publish_state(on);

still an error, Note: there is a midnight factor here 17:30 to 05:30. (17:30 to 23:59 and 00:00 to 05:30)

/config/esphome/esp3.yaml:47:17: error: 'parse_number' was not declared in this scope
             int now = parse_number<int>(id(sntp_time).now().strftime("HHMM")).value();
                 ^
/config/esphome/esp3.yaml:47:30: error: expected primary-expression before 'int'
             int now = parse_number<int>(id(sntp_time).now().strftime("HHMM")).value();
                              ^
*** [/data/esp3/.pioenvs/esp3/src/main.cpp.o] Error 1
========================= [FAILED] Took 61.48 seconds =========================

thank you

using ESPHome version: 2021.8.2. Below code spits out time in HHMMSS format

text_sensor:
  - platform: template
    name: "Current time"
    lambda: |-
      char str[20];
      time_t currTime = id(sntp_time).now().timestamp;
      strftime(str, sizeof(str), "%H%M%S", localtime(&currTime));
      return { str };
    update_interval: 60s

can we use its output to feed time-range logic? im trying different logic…no succees yet

Capture24

Did you solve this?
I had another look at it, and this might work for you:

time:
  - platform: sntp
    id: sntp_time
    on_time:
      - seconds: 0
        minutes: /5
        then:
          lambda: |-
            auto ts_now = id(sntp_time).now().timestamp;
            auto d_now = floor(ts_now/86400)*86400;
            auto s_now = ts_now - d_now;
            if ( ( (s_now > 16200) && (s_now < 23400) ) || ( (s_now > 55800) && (s_now < 59400) ) ) {
              id(relay1).turn_off();
            } else {
              id(relay1).turn_on();
            }

This script checks every five minutes (can of course be adapted) whether the current time falls within either 05:30-07:30 or 16:30-17:30 local time and if so switches the relay off, and else switches the relay on.

  • ts_now is the timestamp for the current time in UTC
  • d_now is the timestamp for the previous midnight in UTC
  • s_now is the current number of seconds since midnight UTC

Please note that this is based on my own local timezone Europe/Amsterdam, and it appeared that the
“now().timestamp” option returns a timestamp in UTC.
So you have to modify the “seconds since midnight” to your own local timezone.
For me it is:

seconds_since_midnight_UTC

1 Like

thank you for the reply.
actually its 01:20 am here. im still pulling my hair over this on the discord channel as well.
i will try this.

That solved it! Thank you very much for the help.
We can set time zone and just input time like 05:30 in “530” format now.

With this code one can make standalone ESP that return to correct state with wifi even though there was a power failure just before a trigger time.

Default state is “on” when boot. Only after connecting to sntp it decides whenther to switch off or not.
This way, if it did not connect to sntp on boot it will not switch_off eg. lights at wrong times(esp boots at year 1969 time 19:00:00)

There you go → a resilient switch, without an always on HA

code below

esphome:
  name: esp3
  platform: ESP8266
  board: esp01_1m

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:

ota:
  password: "e624086750591586d6d0f2d1e3f8c087"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp3 Fallback Hotspot"
    password: "2ytcdVXXwdaJ"
  reboot_timeout: 15min
  power_save_mode: HIGH
  fast_connect: false
  output_power: 10.0

captive_portal:

switch:
  - platform: gpio
    id: relay1
    pin:
      number: GPIO0
      inverted: True
    restore_mode: ALWAYS_ON
    name: "Relay1"

time:
  - platform: sntp
    id: sntp_time
    timezone: UTC-05:00
    on_time:
    - seconds: 0
      minutes: /1
      then:
        - lambda: |-
            int t_now = parse_number<int>(id(sntp_time).now().strftime("%H%M")).value();
            if ( ( (t_now > 530) && (t_now < 730) ) || ( (t_now > 1630) && (t_now < 1730) ) ) {
              id(relay1).turn_off();
            } else {
              id(relay1).turn_on();
            }

1 Like

Well done! :+1:

Two small remarks:

Make sure your timezone is set correctly
“UTC-05:00” in fact means an offset of +5 hours to UTC in this case, so if your local timezone offset would be -5 hours to UTC you have to set it as “UTC+05:00” (the sign has to be inverted).
Also, setting the timezone in the “UTC-xx:xx” or “UTC+xx:xx” format will not take DST changes into account, so you have to correct for this manually if applicable.
Better would be a timezone like for instance “America/New_York” because that would take DST into account, but you have to check if this works in your case, because there currently is a bug in the ESPHome Time integration wrongly interpreting many of that kind of timezone entries.
See also: Time format help

And one improvement for the if statement:

if ( ( (t_now >= 530) && (t_now <= 730) ) || ( (t_now >= 1630) && (t_now <= 1730) ) )

This does not make a difference for your current time periods, but makes it possible to even set time periods of only one minute, like for instance 16:00 - 16:01