How to use on_time trigger with template at Datetime component in ESPHome?

Hi, I try to set up a simple time-based automation. I am to control two fans by 0-10V sign, in times defined by user in ESPHome webserver. This is my yaml, that does not work as I plan:

esphome:
  name: esp32

esp32:
  variant: esp32s3
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: ""

ota:
  - platform: esphome
    password: ""

wifi:
  ssid: "some SSID"
  password: "some password"

# Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "other SSID"
    password: "other password"

captive_portal:

# As I have no HomeAssistant, I use ESPHome webserver.
web_server:
  port: 80
  version: 2
  ota: false
  local: true

i2c:
  sda: GPIO13
  scl: GPIO14
  frequency: 400kHz

gp8403:
  voltage: 10V
  address: 0x5F

# Defining two outputs for two fans.
output:
  - platform: gp8403
    id: gp8403_0
    channel: 0
  - platform: gp8403
    id: gp8403_1
    channel: 1

time:
  - platform: sntp
    id: sntp_time
    timezone: Europe/Berlin
    servers:
     - 1.pool.ntp.org
     - time.nist.gov
     - time.cloudflare.com

# I set up two Number templates for setting the speed of fans, by 0-10V signal 
# of a GP8403 device. One Number is for daytime speed and one for the night.
number:
  - platform: template
    name: "Fan speed 1"
    id: S1
    optimistic: true
    restore_value: true
    initial_value: 50
    min_value: 0
    max_value: 100
    step: 5
  - platform: template
    name: "Fan speed 2"
    id: S2
    optimistic: true
    restore_value: true
    initial_value: 70
    min_value: 0
    max_value: 100
    step: 5

# There are two templateble Datetime.time objects to set up 
# the beginning of daytime and the beginning of the night. 
datetime:
  - platform: template
    type: time
    id: T1
    name: Time 1
    time_id: sntp_time
    optimistic: yes
    initial_value: "10:00:00"
    restore_value: true
    on_time:
      - output.set_level:
          id: gp8403_0
          level: !lambda 'return id(S1).state / 100 ;'
      - output.set_level:
          id: gp8403_1
          level: !lambda 'return id(S1).state / 100 ;'
  - platform: template
    type: time
    id: T2
    name: Time 2
    time_id: sntp_time
    optimistic: yes
    initial_value: "18:00:00"
    restore_value: true
    on_time:
      - output.set_level:
          id: gp8403_0
          level: !lambda 'return id(S2).state / 100 ;'
      - output.set_level:
          id: gp8403_1
          level: !lambda 'return id(S2).state / 100 ;'type or paste code here

It just refuses to work. So, it complies, and seems to have nothing special in the logs, but does not change the 0-10V output (measured by multimeter). Isn’t it the way that on_time trigger should work in Datetime component?

Any help would be appreciated. Thanks,
kiir

Post the logs instead.

Here you are, the logs:

[22:02:00.186][C][wifi:661]: WiFi:
[22:02:00.190][C][wifi:444]:   Local MAC: ................
[22:02:00.192][C][wifi:449]:   SSID: '.........................'
[22:02:00.196][C][wifi:452]:   IP Address: .................
[22:02:00.200][C][wifi:456]:   BSSID: ........................
[22:02:00.200][C][wifi:456]:   Hostname: 'esp32_44'
[22:02:00.200][C][wifi:456]:   Signal strength: -66 dB ▂▄▆█
[22:02:00.222][C][wifi:467]:   Channel: 7
[22:02:00.222][C][wifi:467]:   Subnet: 255.255.255.0
[22:02:00.222][C][wifi:467]:   Gateway: 192.168.1.2
[22:02:00.222][C][wifi:467]:   DNS1: 192.168.1.2
[22:02:00.222][C][wifi:467]:   DNS2: 0.0.0.0
[22:02:00.226][C][logger:273]: Logger:
[22:02:00.226][C][logger:273]:   Max Level: DEBUG
[22:02:00.226][C][logger:273]:   Initial Level: DEBUG
[22:02:00.229][C][logger:279]:   Log Baud Rate: 115200
[22:02:00.229][C][logger:279]:   Hardware UART: USB_SERIAL_JTAG
[22:02:00.231][C][logger:286]:   Task Log Buffer Size: 768
[22:02:00.232][C][i2c.idf:081]: I2C Bus:
[22:02:00.233][C][i2c.idf:082]:   SDA Pin: GPIO13
[22:02:00.233][C][i2c.idf:082]:   SCL Pin: GPIO14
[22:02:00.233][C][i2c.idf:082]:   Frequency: 400000 Hz
[22:02:00.236][C][i2c.idf:092]:   Recovery: bus successfully recovered
[22:02:00.237][C][i2c.idf:102]: Results from bus scan:
[22:02:00.238][C][i2c.idf:108]: Found device at address 0x5F
[22:02:00.253][C][template.number:015]: Template Number 'Fan speed 1'
[22:02:00.255][C][template.number:051]:   Optimistic: YES
[22:02:00.258][C][template.number:352]:   Update Interval: 60.0s
[22:02:00.262][C][template.number:015]: Template Number 'Fan speed 2'
[22:02:00.264][C][template.number:051]:   Optimistic: YES
[22:02:00.267][C][template.number:352]:   Update Interval: 60.0s
[22:02:00.278][C][template.time:103]: Template Time 'Time 1'
[22:02:00.280][C][template.time:104]:   Optimistic: YES
[22:02:00.284][C][template.time:352]:   Update Interval: 60.0s
[22:02:00.287][C][template.time:103]: Template Time 'Time 2'
[22:02:00.289][C][template.time:104]:   Optimistic: YES
[22:02:00.291][C][template.time:352]:   Update Interval: 60.0s
[22:02:00.304][C][gp8403:015]: GP8403:
[22:02:00.304][C][gp8403:015]:   Voltage: 10V
[22:02:00.306][C][gp8403:019]:   Address: 0x5F
[22:02:00.328][C][gp8403.output:013]: GP8403 Output:
[22:02:00.328][C][gp8403.output:013]:   Channel: 0
[22:02:00.344][C][gp8403.output:013]: GP8403 Output:
[22:02:00.344][C][gp8403.output:013]:   Channel: 1
[22:02:00.363][C][captive_portal:122]: Captive Portal:
[22:02:00.366][C][web_server:309]: Web Server:
[22:02:00.366][C][web_server:309]:   Address: esp32_44.local:80
[22:02:00.382][C][sntp:059]: SNTP Time:
[22:02:00.383][C][sntp:062]:   Server 0: '1.pool.ntp.org'
[22:02:00.386][C][sntp:062]:   Server 1: 'time.nist.gov'
[22:02:00.388][C][sntp:062]:   Server 2: 'time.cloudflare.com'
[22:02:00.393][C][esphome.ota:075]: Over-The-Air updates:
[22:02:00.393][C][esphome.ota:075]:   Address: esp32_44.local:3232
[22:02:00.393][C][esphome.ota:075]:   Version: 2
[22:02:00.410][C][safe_mode:018]: Safe Mode:
[22:02:00.410][C][safe_mode:018]:   Successful after: 60s
[22:02:00.410][C][safe_mode:018]:   Invoke after: 10 attempts
[22:02:00.410][C][safe_mode:018]:   Duration: 300s
[22:02:00.414][C][web_server.ota:235]: Web Server OTA
[22:02:00.430][C][api:207]: Server:
[22:02:00.430][C][api:207]:   Address: esp32_44.local:6053
[22:02:00.432][C][api:217]:   Noise encryption: NO
[22:02:00.435][C][mdns:213]: mDNS:
[22:02:00.435][C][mdns:213]:   Hostname: esp32_44
[22:02:54.750][I][safe_mode:042]: Boot seems successful; resetting boot loop counter
[22:02:54.766][D][esp32.preferences:143]: Writing 1 items: 0 cached, 1 written, 0 failed
[22:04:46.929][D][web_server_idf:337]: Removing dead event source session
[22:04:46.931][D][web_server_idf:418][httpd]: Event source connection closed (fd: 60)
[22:20:53.729][D][datetime.time_entity:053]: 'Time 2' - Setting
[22:20:53.732][D][datetime.time_entity:055]:  Hour: 22
[22:20:53.736][D][datetime.time_entity:058]:  Minute: 22
[22:20:53.738][D][datetime.time_entity:061]:  Second: 0
[22:20:53.741][D][datetime.time_entity:029]: 'Time 2': Sending time 22:22:00
[22:20:56.153][D][esp32.preferences:143]: Writing 1 items: 0 cached, 1 written, 0 failed

Your logs don’t present any calls at all…

Do you have some evidence that automation trigger on_time: is correct for datetime?
Do you get errors/warnings when you compile?
Did you try on_value: trigger?

Add some log output commands in your on_time: block. That will at least confirm if the issue is with the datetime: component or with the GP8403.

Thanks for your help and questions.

I don’t have strong evidence for datetime component trigger on_time , as it does not works for me. However it is written in the documentation of datetime component you linked for me, and there are some topics on that, so it should:
https://community.home-assistant.io/t/timer-switch-need-help-with-datetime-component/730337
https://community.home-assistant.io/t/problem-esp-chicken-coop-doors/765865

No, I didn’t get any error/warnings while compiling (apart from one warning on the _ character in file name).

No, I have not tried on_value: trigger yet, as it is not mentioned in the documentation of time or datetime component. I will surely do, if there’s no better idea.

Correct, I wasn’t aware of it.

Sure it is.

I believe on_time was added later to datetime, weirdly it’s not listed under automations.

Add some log output commands in your on_time: block. That will at least confirm if the issue is with the datetime: component or with the GP8403.

I have made some tests. I wrote a yaml in that GP8403 output was controlled by a speed fan entity, and an other one in that they were controlled by a template number. They worked flowlessly – but without any schedule.

And the best is: when I made a yaml with hardcoded schedule (time.on_time), it didn’t make any call (just like datetime.on_time). As you suggested, I added logs, as well, but they didnt fire.

Does my sntp.time section hiccup for some reason, maybe? To be honest, I am stuck.

Log that your time component has valid time time.has_time

If your time: component is set up properly, you should see something like this early in the log:

[09:31:42.935][D][sntp:099]: Synchronized time: 2025-10-15 09:31:42

I just tested your datetime: template and it triggered fine, so I reckon that bit of your yaml is fine.

datetime:
  - platform: template
    type: time
    id: T1
    name: Time 1
    time_id: sntp_time
    optimistic: yes
    initial_value: "9:32:00"
    restore_value: true
    on_time:
      - logger.log: "It's TIME!!!"

[09:32:00.020][D][main:389]: It's TIME!!!

Try reducing the time block to just this:

time:
  - platform: sntp
    id: sntp_time

I can’t see it, really – I might have missed it, ESPHome has quite a big documantation. Could you show me that mention in Time or Datetime components?

Unfortunately, it seems to have no valid time. I wrote a small yaml only for testing time:

esphome:
  name: esp32_44

esp32:
  variant: esp32s3
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: ""

ota:
  - platform: esphome
    password: ""

wifi:
  ssid: "some ssid"
  password: "some password"

# Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "other ssid"
    password: "other password"

captive_portal:

web_server:
  port: 80
  version: 2
  ota: false
  local: true

time:
  - platform: sntp
    id: sntp_time
    timezone: Europe/Berlin
    servers:
     - 1.pool.ntp.org
     - time.nist.gov
     - time.cloudflare.com
    on_time_sync:
      then:
        - logger.log: "System clock is synchronized"
    on_time:
      - seconds: 0
        minutes: 0, 10, 20, 30, 40, 50
        then: 
          if:
            condition:
              time.has_time:
            then:
              - logger.log: "Time has been set and is valid!"
      - seconds: 0
        minutes: 5, 15, 25, 35, 45, 55
        then:
          if:
            condition:
              time.has_time:
            then:
              - logger.log: "Time has been set and is valid!"

interval:
  - interval: 1min
    then:
      - logger.log:
          format: "Time is %.1f"
          args: [ 'id(sntp_time).now()' ]

And the logs:

[00:23:35.999][C][wifi:661]: WiFi:
[00:23:36.004][C][wifi:444]:   Local MAC: ...............
[00:23:36.005][C][wifi:449]:   SSID: ........................
[00:23:36.010][C][wifi:452]:   IP Address: ...............
[00:23:36.012][C][wifi:456]:   BSSID: ......................
[00:23:36.012][C][wifi:456]:   Hostname: 'esp32_44'
[00:23:36.012][C][wifi:456]:   Signal strength: -74 dB ▂▄▆█
[00:23:36.032][C][wifi:467]:   Channel: 7
[00:23:36.032][C][wifi:467]:   Subnet: 255.255.255.0
[00:23:36.032][C][wifi:467]:   Gateway: 192.168.1.1
[00:23:36.032][C][wifi:467]:   DNS1: 192.168.1.1
[00:23:36.032][C][wifi:467]:   DNS2: 0.0.0.0
[00:23:36.044][C][logger:273]: Logger:
[00:23:36.044][C][logger:273]:   Max Level: DEBUG
[00:23:36.044][C][logger:273]:   Initial Level: DEBUG
[00:23:36.047][C][logger:279]:   Log Baud Rate: 115200
[00:23:36.047][C][logger:279]:   Hardware UART: USB_SERIAL_JTAG
[00:23:36.049][C][logger:286]:   Task Log Buffer Size: 768
[00:23:36.087][C][captive_portal:122]: Captive Portal:
[00:23:36.095][C][web_server:309]: Web Server:
[00:23:36.095][C][web_server:309]:   Address: esp32_44.local:80
[00:23:36.106][C][sntp:059]: SNTP Time:
[00:23:36.110][C][sntp:062]:   Server 0: '1.pool.ntp.org'
[00:23:36.111][C][sntp:062]:   Server 1: 'time.nist.gov'
[00:23:36.114][C][sntp:062]:   Server 2: 'time.cloudflare.com'
[00:23:36.118][C][esphome.ota:075]: Over-The-Air updates:
[00:23:36.118][C][esphome.ota:075]:   Address: esp32_44.local:3232
[00:23:36.118][C][esphome.ota:075]:   Version: 2
[00:23:36.136][C][safe_mode:018]: Safe Mode:
[00:23:36.136][C][safe_mode:018]:   Successful after: 60s
[00:23:36.136][C][safe_mode:018]:   Invoke after: 10 attempts
[00:23:36.136][C][safe_mode:018]:   Duration: 300s
[00:23:36.142][C][web_server.ota:235]: Web Server OTA
[00:23:36.156][C][api:207]: Server:
[00:23:36.156][C][api:207]:   Address: esp32_44.local:6053
[00:23:36.159][C][api:217]:   Noise encryption: NO
[00:23:36.161][C][mdns:213]: mDNS:
[00:23:36.161][C][mdns:213]:   Hostname: esp32_44
[00:24:30.118][I][safe_mode:042]: Boot seems successful; resetting boot loop counter
[00:24:30.128][D][esp32.preferences:143]: Writing 1 items: 0 cached, 1 written, 0 failed
[00:24:34.322][D][main:367]: Time is 0.0
[00:25:34.324][D][main:367]: Time is 0.0
[00:26:34.326][D][main:367]: Time is 0.0
[00:27:34.331][D][main:367]: Time is 0.0
[00:28:34.329][D][main:367]: Time is 0.0
[00:29:34.334][D][main:367]: Time is 0.0
[00:30:34.338][D][main:367]: Time is 0.0
[00:31:34.341][D][main:367]: Time is 0.0
[00:32:34.343][D][main:367]: Time is 0.0
[00:33:34.473][D][main:367]: Time is 0.0
[00:34:34.344][D][main:367]: Time is 0.0
[00:35:34.349][D][main:367]: Time is 0.0
[00:36:34.346][D][main:367]: Time is 0.0
[00:37:34.348][D][main:367]: Time is 0.0
[00:38:34.354][D][main:367]: Time is 0.0
[00:39:34.353][D][main:367]: Time is 0.0
[00:40:34.351][D][main:367]: Time is 0.0

So that is the reason. From now on, my question is not about on_time trigger in Datetime component, but Time component SNTP platform: why I don’t get time from SNTP servers.

Possibly your router (or your ISP) is blocking the SNTP port. Do you use Home Assistant? Try using the HA time source.

https://esphome.io/components/time/homeassistant/

It pops up if you open the link I posted. But it was relevant only for debugging.
Now that you don’t have actual time, try to play with your router settings or try HA time like @zoogara suggested.

I don’t have HA, and I guess my router has nothing to do with (there are some Shellys and Tasmotas at home, and they all get time from NTP servers).

However, you helped my thinking to the right track: as I succeeded with setting up sntp.time, everything started working like a charm.

Thank you all, men.

It turned out that my device didn’t get time from SNTP servers, and that was all the problem. I set up manual IP and DNS, and miracolously everything works now.

datetime.on_time trigger is a more simple way for dinamic scheduling, than juggling with lambdas. I like it already.

Thank you for all your tremendous help!

Static IP is always preferred. Also with Shelly, believe me…

Yes, I always use static IP addresses, but usually fix them in router. Is’s just more convenient to manage then in one place. Now I had to break that rule with this last ESP32 device :frowning: