What kind of component or entity do I need for this?

How is MainSwitchDelay defined (share the yaml)? Typically you’d just use the relevant switch or binary sensor condition.

MainSwitchDelay is a global bool:

globals:
  - id: TestText
    type: String
    initial_value: '"Original Text"'
  - id: MainSwitchDelay
    type: bool
    initial_value: "false"

The problem is the delay, itself, is not from a sensor. I have this on MainSwitch (lots of debugging statements in here, still):

binary_sensor:
  #
  #Toggle switch group first, with master switch first in that group.
  #
  - platform: gpio
    id: "Mainswitch"
    name: "Main Switch"
    web_server:
      sorting_group_id: sorting_group_master
      sorting_weight: 1
    pin:
      number: ${MasterSwitchPin}
      mode:
        input: true
        pulldown: true
    filters:
      delayed_on: 10ms
    device_class: power
    on_press:
      - logger.log:
          format: "DEBUG PRESS 1: Test Variable Value: %s"
          args: id(TestText).c_str()
      - logger.log:
          format: "DEBUG PRESS 2: Test boolean value: %d"
          args: 'id(MainSwitchDelay)'
      - globals.set:
          id: MainSwitchDelay
          value: 'true'
      - logger.log:
          format: "DEBUG PRESS 3: Test boolean value: %d"
          args: 'id(MainSwitchDelay)'
    on_release:
      - logger.log:
          format: "DEBUG RELEASE 2: Test boolean value: %d"
          args: 'id(MainSwitchDelay)'
      - logger.log: "DEBUG RELEASE 2:    <== Switch released, calling script"
      - script.execute: MainSwitch_Delay_Script
      - logger.log: "DEBUG RELEASE 3:    <== Script returned"
      - logger.log:
          format: "DEBUG RELEASE 4: Test boolean value: %d"
          args: 'id(MainSwitchDelay)'

So when MainSwitch is turned on, of course its state will be ON. When it is turned off, the sensor for the switch itself does not go to OFF. Instead it runs the script with the delay (again, a lot of debugging statements still in here):

script:
  - id: MainSwitch_Delay_Script
    mode: restart
    then:
      - logger.log:
          format: "DEBUG SCRIPT 1: Test boolean value: %d"
          args: 'id(MainSwitchDelay)'
      - delay: 5 seconds
      - logger.log:
          format: "DEBUG SCRIPT 2: Test boolean value: %d"
          args: 'id(MainSwitchDelay)'
      - globals.set:
          id: MainSwitchDelay
          value: 'false'
      - logger.log:
          format: "DEBUG SCRIPT 3: Test Variable value: %d"
          args: 'id(MainSwitchDelay)'
      - logger.log: "DEBUG SCRIPT 4: <-- Delay script ending"

MainSwitchDelay is true when the switch is turned on and stays true for, in this test, 5 seconds after it’s turned off. While this value is related to the state of MainSwitch, its value is not always the same as MainSwitch. So what I need to publish is not the state of a sensor, but the state of the delay. That’s why I’m making the Sensor Template dependent on a bool that I can turn on when the switch goes on and turn off later, after the switch goes off.

The two things I’m still trying to work out is how to use the value of a template number in a statement like this, to replace the fixed value of 5:

- delay: 5 seconds

and I need to have the delay available for 4 switches and 4 sensors. Since I can only use arrays for C++ variables, like bool, int, and String, I can’t have arrays of Binary Sensors, Number Templates, and scripts. (As best I can tell, I can’t make a script use paremeters, so I can’t call a script with a value to change.)

(Side note: On the control board, I have DTPST switches to turn on the board, the CNC, the laser driver, and the laser itself. One pole of each switch controls a device, like the CNC, the other is connected to a GPIO. In the code, I call these switches, but the component in ESPHome is a Binary Sensor. I do not want something like a CNC or an etching laser to be accessible from the ESPHome or HA interface. You can imagine the mess if even an etching laser was somehow accidentally turned on through HA and was not turned off. I want only direct physical control of these systems.)
.

Can’t you just do something like this?

binary_sensor:
  - platform: gpio
    id: "Mainswitch"
    name: "Main Switch"
    web_server:
      sorting_group_id: sorting_group_master
      sorting_weight: 1
    pin:
      number: ${MasterSwitchPin}
      mode:
        input: true
        pulldown: true
    filters:
      delayed_on: 10ms
    device_class: power

  - platform: copy
    id: MainSwitchDelay
    source_id: Mainswitch
    filters:
      - delayed_off:  !lambda return id(delay_seconds).state * 1000.0;  # Convert to ms


number:
  - platform: template
    id: delay_seconds
    name: "Delay Seconds"
    optimistic: true
    min_value: 0
    max_value: 100
    step: 1
    initial_value: 5

Edited wrong: source_id:

Would that include something to work like mode: restart does? That’s a critical part of it, in case I find I have something else to do while the delay is counting down, and I turn it back on, I do need it to restart the count.

Should do.

If an ON value is received while waiting, the OFF action is discarded. Or in other words: Only send an OFF value if the binary sensor has stayed OFF for at least the specified time period. When using a lambda call, you should return the delay value in milliseconds.

Globals be handy for many applications. But you might find they can be harder to debug and work with than thier component equivalents (where they exist). Components can be marked as internal: true if you don’t want them accessible outside of ESPHome (you might want to confirm that accessibility bit).

Another issue with components, and ESPHome, in general: I’m finding that it’s really hard to know what to treat as a C++ object/component and what to treat as part of YAML. In this case, I have a global variable. In any other language, I’d refer to that variable with its name. If it were switchDelay, I’d use “switchDelay” to use the value in it. Maybe, in some languages, I’d use something like “$switchDelay.” Here, to get the value to print it in logs, I use ‘id(switchDelay).state’. Has to be in single quotes. If I define the value for it, as initial_value=<something> then, if it’s a string, it has to be done like this: ‘“text_for_initial_val”’ - enclosed inside with double quotes and outside with single quotes. But the YAML standard is that, after the colon and space, you don’t even need quote marks. But you have to know it’s passed on to the compiler and has to be enclosed that way. So, while writing any YAML file for ESPHome, you have to know when you’re using something that’s in C++ and when it’s in YAML.

Maybe long time C++ coders know that easily, or maybe those who designed ESPHome know it without thinking about it, but some of this stuff is either not always documented, or examples are not given. For instance, I need to use a Template Number for switchDelay. There are examples of Template Numbers, but NOT an example of how to access the value or an explanation of why you can’t just use switchDelay.value or switchDelay.state when you need it. Sometimes it’s just enclosing it in single quotes, other times, apparently (I’m getting help on this issue - still trying to make it work), it requires a lambda to return the value for a command to use. It’s totally counter-intuitive to have to get the value one way to print it in a log statement and another way to use the value in a calculation or command.

So, yes, keeping it local, is a good thing - avoids having to refer to any globals! I’m going to look this over. I have this other test to finish (accessing the value in a Template Number) and, after that, I’ll check into your code - if it does what you’re pretty sure it does (and restarts on a new button push), I can use it. (I’m also going to look up the copy platform - is that a way to use inheritance? Hope so! That’d make some of what I’m doing with multiple sensors and switches that can all do the same thing much easier to do!)

And if I’m missing something, I’m going over this and another thread and putting things together in my head, so there’s a back and forth going on here and I’m trying to assimilate a lot for this. (And it’s frustrating - if I were using something like Python or Java, or most other languages, I think I’d have it by now, but YAML on ESPHome seems an odd mix of configuration and C++.)

1 Like

I’m reading up on Copy Component now and I’m not clear about a few things, especially in how they relate to what’s going on here.

When you use -platform: copy, does that mean a copy of MainSwitch is made at compile time, or that it’s a copy that continues to “track” all the values in MainSwitch “live,” as it changes from on to off? The way you’re using it implies that it’s a live copy, updated continually as MainSwitch is updated and changes. Also, since the -delayed_off filter is specified by the Number Template (I keep forgetting whether it’s ‘Template Number’ or ‘Number Template’), that makes me think that the copy of MainSwitch is a real-time mirror of it - just with the delayed off filter added to it.

(And, FYI, your use of the lambda for that confirms exactly what I’ve been asking about in another topic. Apparently you can’t use a lambda to get a number, then use a time specification after that - seems like a lambda can’t have anything after it on a line.)

That is correct.

Did that lambda delay work? I think it should have but it is untested.

Yeah it isn’t always so clear that if you use a lambda in some time fields it needs to be in ms. There is some hints around the place though.

I would say it isn’t always clear even for more experienced users and you end up searching for a working example or trying a few common variations until something works.

For example I was sure there was a way to do this but had to search the forum to find an example that (hopefully) works.

You can always just do your entire project in c++ and avoid esphome altogether.

The lambda worked - exactly the same as suggested in that other thread and made at about the same time. (Or, when I came back and looked at both threads, I think both your post and the one using the same lambda in the other thread were both there for me to read on that return visit.)

I haven’t had time to test your solution yet, but there are several things I like about it, One, that isn’t a direct suggestion from what you did, is that I don’t need a boolean to turn on and off. I’m going to make the new version (using a lot of what you did) and make the default delay value 0. That should work as well as using a bool and turning it on and off. With a delay of 0, then it just eliminates the delay by making it not happen. Also, you use a Binary Sensor, a copy of the same, a Number Template, and that’s it. I use a Binary Sensor, a Binary Sensor Template, a Number Template, a Script, and a global bool. (And I’m not big on globals, other than as constants, since it pollutes the namespace and can cause confusion.)

I think the reason I was getting errors is because (and this is a guess) once you specify a lambda on a line, you can’t really follow it with anything else because there are no limiters or delimiters for a lambda. (Maybe that would be worth considering adding as an option?)

I hate to criticize documentation and the people who write it, and I don’t mean this at all as a slam, it’s an attempt at constructive criticism. However, I think there are serious gaps in the documentation in this project. Often that’s in the form of examples. And what we’re dealing with is one: A Template Number. So how do you use it once you’ve configured one? In the thread I linked to above, we found that to use the value of the Template number in logger.log, you use one format, but to use it as a value in a statement (like the delay statement), you need to use a lambda - AND you need to be sure the lambda is not followed by something like the timing unit specification. Instead, multiply the returned amount by 1,000.

That’s a lot that’s not documented. How is one going to know all that about using a global variable? And why can’t a variable be just used directly? Unless you’re directly involved in this project, or have a decent amount of experience, how to use a global variable is a mystery. (At least now Google should show these discussions on it, which, I hope, helps people in the future.) There is no reason to expect a user, especially a new one, to know all these complexities about using a global variable instead of just using the name of the variable.

Even more, you knew what to search for. For a lot of my questions, I’m not always sure just where to search or what terms. For instance, for the formatting for using my Number Template in a log entry, one person found it in the log documentation. I didn’t look there because I thought it was more a question of, “How do I use the value of this object like I’d use a variable?” I figured there was a way to reference it in any situation. I had no reason to think it should be handled one way in logger.log and another in a statement like delay. I’ve been programming, off and on, since the 1970s, and have never seen a situation where you have to do so much to use the value of a variable in different situations.

I always find comments like that puzzling, since, really, when anyone starts out planning or writing a project, they know there are a number of choices available to them and we’re all pretty strongly aware no solution is going to be perfect. Addressing issues or stating concerns about a project is not saying it’s unfit or terrible. It’s actually giving the project maintainers a chance to improve it. At this point I’m not clear if ESPHome is meant for, basically, C++ programmers, seriously advanced developers, or if there’s a plan to make it (eventually) usable by advanced Home Assistant users who know how to write YAML files. Maybe it’s one of those “We’ll write what works for us, put it out there, and deal with the people who think like us and ignore the rest” kind of projects. I don’t know. But the issues discussed here are an opportunity for improvement. If I understood more of this system, I’d be glad to help out. I’ve found several issues in documentation in the past 1-2 weeks I’ve been exploring ESPHome. (I think you fixed one of them.) My response, above, to @Mahko_Mahko, is an example of directly addressing one issue that could be solved.

When I started planning this, I thought I could write my own code in C++ for ESP32, or do it in Python for a Raspberry Pi and do a REST and web interface. It wouldn’t be a neat and clean web interface, but I could throw one together. (Although the last time I had to do any programming that used Javascript and HTML, AJAX wasn’t even around!) I listed the issues I would have to solve and felt it would take weeks longer if I did it all in a Pi, since there’s really nothing out there to easily turn a Pi into an HA device. (Sure, you can use it as a hub, but as something like a control panel or a safety sign? Those would require creating an HA integration, planning a REST interface, and implementing all that. (And those are just a few issues to solve -there’s more.)

I picked ESPHome since it has a REST interface, works with HA, and can be used to drive a small monitor.All of those would take a lot of time to solve if I created my own system on a Pi. What is frustrating, at times, is how much time it can take to find out simple things about ESPHome (like the referenced issue about what the format is to use a global variable in different situations).

I think I found that over here.

The good news is that anyone can contribute to improving the documentation.

It’s at the bottom of most pages.

It might be worth doing a bit of that while the new user experience is still fresh in your mind. You learn and accept the nuances pretty quick.

I made the comment because you seemed more comfortable in c++ and obviously miss some parts of c++.

Also if you did use c++, or a pi, you’d probably use MQTT rather than create a rest interface.

My two cents on this is that it is targeted at people with a non c++ background so they can easily build many common basic projects. I think it does a great job of that.

Since there is C++ under the hood then there is nothing stopping you from doing whatever hybrid of ESPHome and full C++ based solutions you want. Although the precise way to do that may not be well documented.

The people that come from a strong C++ side of things seem to more just know how many advanced things work and “figure it out”. Baffles me.

Probably you’re somewhere in the middle where you have a fair bit of the c++ but can’t always figure out if you should do it the c++ or yaml way, or how the hybrid looks. The yaml way can get you quite far once you know “the ways”.

BTW have you found the ESPHome Discord yet? That’s where the bulk of advanced users are and they might be able to answer more of your architecture questions.

I honestly find the ESPHome documentation better than the Home Assistant docs!

Yes - good find! I tend to have search issues, since I tend to pick terms that narrow a search as much as possible. Old habit I can’t stop and it often means I don’t get hits others find easily.

I missed the “Edit” link - I think I’ve been so focused on just finding answers that I tuned out what didn’t lead me to more info.

That’s an excellent point! I’ve been using open source since the '90s and have been contributing, in some small forms, one or two projects of my own that have been outdated, for almost as long. Often, as a newbie in a project, suggestions are ignored or glossed over. Often there’s a, “Wait until you know what’s going on and it’ll all make sense,” attitude (or a, “No, we’re doing it this way because it’s right and others are wrong!” attitude - less frequent, but I’ve run into it).

As a beginner on with a project, it can be hard to contribute to docs. There’s a feeling of, “But if I write this, what if I’m overlooking or missing something important?” But this discussion is proof people here are open to bringing up questions like, “This seems more complete. Is there anything inaccurate in it?” on this forum.

I think writing up something for the Template components would be helpful. At this point, I can do that for the Numbers one. I don’t know if the Text or Select would work the same. I also wish I could find the page discussing Number Components (or Template Number) that mis-stated what the state field does. It wasn’t on the main pages for those components, but I did read something about it that was confusing.

I take it if I started threads with something like, “Possible issue in the documentation, suggested change for ” would make sure I get feedback before posting misinformation.

I did make one post about an issue with what seemed buggy behavior - included something like “Possible Bugs” in the title, but never had responses on it. If it’s not bugs, that would also provide me information for adding something to the docs.

Very astute of you. I have used C++ in the past, but that was over 15 years ago. I loved it, since it was “closer to than metal” than most languages. (I used to do a lot in Assembler, back when Apples ran on 6502 chips!) There’s a part of me that would really love the excuse to dive back in, since I’ve forgotten so much! (The hardest part, for me, was learning how to use all the options for compiling and linking - the programming was fairly easy, the “meta-programming” was hard for me to keep straight.)

If I did it on a Pi, I’d use Python for development speed. I keep forgetting about MQTT, for some reason. I have devices that use it and, at one point, I had read up on it, but I just keep forgetting that’s an option!

One advantage to an ESP32 is no OS on board - so nothing running updates in the background or daemons you forgot about getting in the way of an embedded system. I’m hoping to get used to using ESPHome and Pis for some projects I’d like to do for home automation and just a few simple gadgets.

That would make sense. Still, it’s hard to do much without lambdas and that means that to do much on it, one needs a smattering of C++. I think lambdas are basically filling in the gap for functions and services YAML doesn’t supply.

I would freaking love to! I would really love to add something that makes it easy to reference and use variables and substitutions, and getting the values of Components, without having to use lambdas. But it’s been so long since I used C++, and right now I have to get a business up and running, so the time is an issue. (I’d certainly be willing to get into discussions and get back up to speed on C++ over time.)

I think that’s why some things don’t get fixed, and it goes back to @nickrout’s comments about the perspective of a newbie being useful. This is common in open source projects: Those heavily involved know it all and find it easy to navigate, but those who are new are confused - and the highly experienced devs are so used to what they can use easily, it’s easy to forget how hard it is to come in to it fresh.

Somewhere along the line, I told myself, “Okay, YAML is more like a config language, but it’s really a limited form of OOP.” That helped me a lot. Here, it’s confusing since some fields are YAML and don’t even need quotes around them to use them, and others are C++ and need quotes or even the “double quotes inside single quotes” thing for string values. It’s tough to know which is which without experience.

I haven’t been there yet. I’ll have to keep that in mind. I often do better using non-realtime forums because it takes me time to think through a post.

I have a love/hate relationship with HA, but I like it better than OpenHAB, where I found the forum frustrating because of negative responses to me when I needed help with clarifications of how things were said and just what they meant.

1 Like

While the component issue is solved, I just realized there’s another issue with this situation: Detecting the state on startup. I’m using toggle switches, not push-button, so if there’s a power interruption or something like that and power comes back on, and the switch is on, it won’t detect that.

For the fan, that’s important, since, if the laser turned on in a situation like this, it’d be important to have the vent fan turn on to keep the workshop from filling with smoke. Also, with that kind of situation, I’d want to get an urgent warning that the control box came back on and the laser is on. I’m thinking of several solutions for this:

  1. Setup something in on_boot to check the state of each switch and, if it’s on, to set the delay as true.
  2. When power the system comes on, on_boot could check the main switch and, if it’s on, disable the entire box until that main switch gets turned off then on again.

#2 is more likely, but it also raises an issue because I do not want a control box for devices like this to be able to be turned on or off remotely. I’m thinking I’d have to make the deactivation part to be something internal, that can’t be controlled externally. (That would make sure it can’t be turned on remotely, through HA, by mistake or by eager and curious grandkids.)

While this situation is unlikely, since I’m not going to be using the CNC or laser remotely, and if there were a power loss (we have a standby generator and devices like this are on a UPS), the first thing I’d do is to hit the main switch on the panel to shut it down. BUT - this is the kind of situation where something bad could happen.

I can’t recall your specific implementation, but take a look at publish_initial_state:.

I’m not worried about what’s published - I’m worried about making sure devices can’t come on if there’s a power outage and the switches were left on and are still on when power comes back on.

I’ve posted about using on_boot and how long ESPHome takes to boot. It seems to me to be over 60 seconds, but I’m not sure. So counting on ESPHome to us an on_boot function for an emergency shutdown on reboot would be a problem. Also, since ESPHome is only reading the switches, which are DPST, so the devices they control are on a different circuit, I think I’m going to have to handle this with hardware.

It looks like your suggestion is working. I did several tests on it today (took me a few days! I like how it’s behaving. When I test and debug something, I’m aggressive with it. I WANT to break it NOW so it won’t break later. (When I was running a software-as-a-service business, this is one thing that helped me sleep better - knowing that I was rougher on my software than my clients would ever be!)

I used a timer component to print the value of the switch and delay regularly so I could track them. I also used another button so I could press it and get a status on them, and had log statements showing me what was going on, so I could easily track the delay and switch. Then I tried to break it. I did things like hold down the button and change the delay value while the button was down. I tried changing the delay while the delay was in progress. I tried pressing the switch, letting up, then pressing again while the delay was ongoing.

I found changing the value of the delay once the delay had started didn’t affect the delay length. (Changing it while the button was down did change the delay.) If I pressed the button again, while the delay was ongoing, it would reset to when I released the button a 2nd time.

So, overall, it’s working fine. I’m going to run more tests on it to see if I can break it, but I think it’s solid and it uses fewer components and less code than my method did.

Also, I mentioned the issue with what to do when I lose power and forget to turn things off. Looks like I can deal with that with a reset button and a simple circuit with an SCR. Basically, once the control box receives power, it will have to be “activated” by pressing a button. That way the ESP32 will work when power comes back, but none of the devices can be turned on until I press the activation button. It’d be nice of that safety feature were transparent, but, for now, this works.

(And as I type that, I can imagine a way I might be able to make it transparent - have to think about that, but it’s another topci.)