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

This is tied in with using a delay. I want something that acts like a boolean, but I’m not sure of what to use for scope and access.

When I turn a switch on, I want the boolean to be true. Then, after I turn the switch off, and the delay has ended, I want the boolean to change to false. I’m not worried about how to set it or read it. The problem is I want a component that HA can read and, if possible, I can see on the web page and using REST to read JSON data. But, as for as access to it, I need:

  • to be able to change it from within this ESPHome device
  • to be able to read it from Home Assistant, the web server page, or using REST to read JSON data
  • to be UNable to alter it using REST, a template on the web page, or through HA.

I don’t know what to uses or what to do to use a value I can edit locally, display outside of the system, but make it also uneditable from outside the device.

I think you simply want a switch. When it is on in HA it will beon in esphome, and vice versa. Stop overthinking.

But is there a way to make a switch so it can’t be turned on and off by HA or the web page?

Seems like a template binary sensor would meet your needs:

1 Like

There’s some examples in this config of how to set up a template binary sensor which only gets updated from “manual calls” elsewhere in the code.

Search it for batt_level_recieved for example.

I don’t think binary_sensor.template.publish can be called from HA, only inside ESPHome.

The examples in the ESPlanty helped. I finally set up a Binary Sensor Template like this:

binary_sensor
  - platform: template
    name: "Switch 1 Delay State"
    id: "Switch1_Delay_State"
    lambda: 'return id(MainSwitchDelay) == true;'

MainSwitchDelay is a bool. I’m surprised there’s no simple way to just check if a bool is true or false. Maybe there is, but I couldn’t find it - which means someone will post a link to an obvious page in the docs about it that I missed!

Anyway, I also did this while experimenting:

time:
  - platform: sntp
    on_time:
      - seconds: /3
        then:
          - if:
              condition:
                lambda: 'return id(MainSwitchDelay) == true;'
              then:
                - logger.log: "DEBUG CYCLE 1A: Delay TRUE"
              else:
                - logger.log: "DEBUG CYCLE 1B: Delay FALSE"

The intent here was NOT to just print the status, but to make sure I could use the bool I created as a condition for branching. I would have thought, since MainSwitchDelay is a bool, that all I’d have to do is use it as a condition, but that didn’t work. (Or does it and I just might have used the wrong punctuation?) I would think there’s an easy way to have a bool work as a condition without having to use a lambda. Is that possible?

But back to the template: It worked fine the way I have it above. I did, of course, have to create a global bool for it. I watched the logging every 3 seconds and any messages of changes of the bool variable I was using to verify it was changing back and forth between true and false as needed.

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.