Lambda Function for Delay?

I’m looking to for an available lambda (C++) function I can use that works like the delay action

So instead of:

on_value:
  then:
	- if:
		condition:
		  lambda: |-
			if (condition) {
			  return true;
			} else {
			  return false;
			}
		then:
		  - switch.turn_on: relay
		  - delay: 500ms
		  - switch.turn_off: relay

I’d like to do:

    condition:
      - lambda: |-
		  if (condition) {
			id(relay).turn_on();
			delay(500ms)
			id(relay).turn_off();
		  }

In C++ it looks like this is the sleep() method - also see here

Looking at the ESPHome API the closest I could see is DelayedOnOffFilter()

Things I’ve unsuccessfully tried:

* sleep(3)
* sleep(3000ms)
* std::this_thread::sleep_for(std::chrono::milliseconds( 15000 ))
* Concurrency::wait(15000);
* esphome::binary_sensor::DelayedOnOffFilter::DelayedOnOffFilter(500ms)

You probably don’t want to load the actions into the condition section.
One is for making the decision to act, not where the actions - or the delay - should reside.

Wherever possible, stick with the higher-level semantics (i.e. do in YAML whatever’s possible) and keep the lambda’s as short as possible, to help preserve forward compatility.

Hi @glyndon, thanks for the reply. I do understand your advice but I don’t want your response to distract from my initial question in case someone else has a solution for a delay() like lambda method

Such a method would simply the more complex solution I ended up with and could prove to be super useful option if someone can confirm if it’s possible

For a little more context, a slightly simplified description of my use-case involves a text sensor that:

  • Checks if “home” or “away” has changed since the last check - this is based on a global string that stores the last value where home/away is based on the distance returned f/ another sensor
  • If the status has changed (f/ home to away or vice versa): trigger a switch to on, delay for 1/2 second, trigger a switch to off
  • Return “home” or “away”

Accomplishing this via:

  • A lambda is a really intuitive and simple solution for this (IF a delay() method were available)
  • YAML is more complex - I needed 2 lambdas: one that determined and returned home/away which triggered a 2nd lambda in “on_value” that had the same logic but includes the lastLocation global/static string to return a bool indicating if the location (home/away) had changed where if true, the then: could kick in were I could add the needed yaml -delay

As a side note, it’s concerning to hear that you’re indicating that using lambdas may be less backward compatible than YAML

  • A little more context here, this is my very first project using ESPHome so I appreciate any insight especially when it comes to best practices such as using YAML vs lambdas and backward compatibility

Anytime you reach below the abstraction layer of a system, you risk doing things that may someday not port forward well. It’s just a concept, not a rule.
Also, there may be a delay method at the YAML level, so your solution may be something like:
condition:
thing to decide
then:
lambda to call some action
yaml call for delay
lambda call for another action

The idea here is to stay as high-level as possible, whenever possible and practical, and resort to lambdas in as atomic a fashion as possible. i.e. keep most of your logic as high-level as is reasonable to do.

Thanks again @glyndon for your response, I’m a little confused by your response it seems you’re suggesting that I may be able to leverage a YAML call for delay?

  • If so, my original post includes the actually YAML call for a delay so I know that exists, and my 2nd post explains why I want to use a lambda method instead of that YAML -delay.

Sorry if I misunderstood you’re response

Note: Good advice regarding risk going beyond the abstraction layer though I’m guessing that since usage of lambdas are pretty well documented I’m hopeful that it shouldn’t be any riskier than using YAML. Of course I say this admittedly stating that I’m new to ESPHome and would defer to the advice of others far more experienced than I.

I’m new at ESPHome too, so please don’t consider my advice too seriously.
While I’ve been programming since the 70’s, most of what I’m espousing here are based on some general truisms across many languages, systems, and decades (and some of which is fraying around the edges).
I believe that I saw a delay call in a YAML-level description of an action in some other integration’s documentation. My appeal to use it is based on the above; that whenever possible, handoff to the overarching framework since doing so will get carried forward as the framework evolves. Any lower-level calls remain outside the framework’s scope, and so someday they may be doing [what is potentially in that future time] the wrong thing and you don’t want to find that out the hard way.
So while the use of lambda’s will continue to be supported, what you do inside one may break as the framework evolves. Lambda is basically ‘escaping’ from the framework and messing at the underlying layer. Which is great, but must be done with caution, for the reason I mention.

Here’s where I saw the YAML call for delay, under the ‘Actions’ subheading:

Thanks again @glyndon, but as previously mentioned, I am aware of and included a link to the documentation for the YAML delay, and also included a written example of it in my original post.

So my originally posted question still remains…

1 Like

The delay in yaml notatin does not actually stop the execution there. You can not use someting like sleep or the Arduino delay. That will stop all of ESPHome.

(some simplifications) ESPHome works by running an endless loop every few milliseconds. Every component including the internal stuff like API to HA and so on gets a short chance to do its sutff then the next loop iteration hapens. Calling a lambda action is also part of this loop. If you would add a call to delay (the Arduino method (you could to it) ) for 10 seconds everything in ESPHome would stop for 10 seconds breaking stuff that is 100% not the way to do it.

The yaml delay action does not actually stop the execution it just sets a callback with a timestamp in the future that gets called later (in a future loop iteration). In theory you can execute a delay in the same way the yaml will trigger it see: esphome/base_automation.h at bf1885af3f5045a43dce38339485326678fc4b44 · esphome/esphome · GitHub

But: It is not that simple and it is no good idea to use this very internal stuff of ESPHome.

you can have multiple lambdas after a then. So you can do:

then:
  - lambda: |-
       id(relay).turn_on();
  - delay: 5s
  - lambda: |-
      id(relay).turn_off();
4 Likes

@danielw, thank you! Your response is so thoughtful and insightful, I really appreciate:

  • The insight about how ESPHome works (your description of the loop and what happens during that loop including and lambda code we write)
  • Why calling the Arduino method f/ a lambda would delay everything in ESPHome (which would change expected behavior)
  • How the YAML delay action works (and doesn’t actually stop execution)
  • How I could (in theory) actually duplicate what the YAML delay action does per this reference though understandably probably not a great approach

Having multiple lambdas after a “then” statement is cool too but not necessary to meet my needs which were met by what I originally posted (see the “So instead of” section)

Again, thanks so much for such a great response! Like I said, I’ve leaned from it and I’m guessing others may as well.

danielw’s suggestion is also a perfect example of what I was (poorly) attempting to say above; to always use the framework (rather than more lambda code) when possible. Here he danielw shows that happening. The lambda does the low-level stuff, but then returns to the higher-level framework for the delay, then back ‘down’ into lambda for the next action. It lets ESPhome handle how the delay gets implemented. That’s the goal.
I’m beyond glad that this worked out for you and you got the answers you needed.