Expose an ESPHome Template Switch as a "light" entity to Home Assistant?

I’m using the configuration below to control a big latching contactor* used to switch a large bank of high bay lights off/on in a manufacturing facility.

What I need help with is figuring out how to tell Home Assistant that this contactor is not a switch entity, but a binary on/off light entity. It is pretty trivial to do this with on/off light switches in zigbee2mqtt’s configuration.yaml, but I can’t figure out how to do it for ESPHome devices.

switch:
  # Contactor template switch (this is what I'd like to expose to Home Assistant as a light):
  - platform: template
    name: None
    turn_off_action:
      - switch.turn_on: output1
    turn_on_action:
      - switch.turn_on: output2
  # This lambda is used to derive the state of template switch from the physical open/closed state of the contactor, which is sensed by monitoring an input that is wired through a spare/auxiliary contact:
    lambda: |-
      if (id(input1).state) {
        return true;
      } else {
        return false;
      }
    # The default restore_mode, "ALWAYS_OFF", will result in the "turn_off_action" at boot time, so we disable it.
    restore_mode: DISABLED

  # Output 1 (this small relay is pulsed to command contactor to the open/off position):
  - platform: gpio
    pin: GPIO13
    name: "Output 1 Relay"
    id: output1
    interlock: [output2]
    interlock_wait_time: 500ms
    on_turn_on:
    - delay: 200ms
    - switch.turn_off: output1
    internal: true

  # Output 1 (this small relay is pulsed to command contactor to the closed/on position):
  - platform: gpio
    pin: GPIO12
    name: "Output 2 Relay"
    id: output2
    interlock: [output1]
    interlock_wait_time: 500ms
    on_turn_on:
    - delay: 200ms
    - switch.turn_off: output2
    internal: true


binary_sensor:
  #Input 1 (used to monitor the contactor open/closed state):
  - platform: gpio
    id: "input1"
    name: "Input 1"
    pin: GPIO5
    #debounce:
    filters:
      - delayed_on_off: 50ms
    disabled_by_default: true
    internal: true

* For searchability purposes, a latching contactor or latching relay is sometimes alternatively referred to as a “bistable contactor”, “bistable relay”, “mechanically-held contactor”, “dual coil contactor”, or “dual coil relay”.

Ps. Before anyone suggests that I use HA’s “Change device type of switch” helper, I don’t want to use that because it has several downsides:

  • The “hidden” switch entity only seems to be hidden for purposes of auto-populating dashboards. Other than that, it’s still visible everywhere, as far as I can tell.
  • It adds more clutter and useless state tracking to the logbook.
  • It adds clutter to the entity registry.
  • It creates additional work and potential breakage whenever you need to refactor your device/entity naming scheme.
  • It seems to break when you use the UI to rename a device and are subsequently asked if you want to rename the associated entities. (even though both the switch and the helper are being renamed at the same time, the renaming code doesn’t seem to know to update the target of the helper)

I’m not a ESPHome wizard, but shouldn’t it be pretty much the same as your switch, just under the binary light platform

light:
  - platform: binary
    on_turn_on:
      - output.turn_on: output2
    on_turn_off:
      - output.turn_on: output1
    lambda: |-
      if (id(input1).state) {
        return true;
      } else {
        return false;
      }
    restore_mode: DISABLED

If you don’t want the switch visible in HA, you can add internal: true in your template switch configuration.

Unfortunately a binary light requires an output: - you could combine your idea with a dummy template output I suppose. Also you would need to include your lambda in an on_state: trigger, and restore_mode: DISABLED is not supported:

light:
  - platform: binary
    name: "Light"
    on_turn_on:
      - switch.turn_on: output2
    on_turn_off:
      - switch.turn_on: output1
    on_state:
      lambda: |-
        if (id(input1).state) {
          return true;
        } else {
          return false;
        }
    output: light_output

output:
  - platform: template
    id: light_output
    type: binary
    write_action:
      lambda: return x;

Maybe you could move the code to the template output and do more in lambda.

1 Like

I also suspect this will cause some issues…

Don’t know the coil circuit, but sounds high for latching relay.

Thanks

Yes, it does. Specifically:

error: return-statement with a value, in function returning ‘void’ [-fpermissive]

The same goes for the lambda on your dummy output example (irrespective of the fact that ‘x’ is undeclared in the scope).

Thanks for trying though! Any other ideas?

It looks like this approach would only work for the simple use case where the microcontroller is using a single output and has exclusive control over the state of lighting circuit.

I guess you might need to be clearer in what you want then.

I heard, “make a switch a light”

Yeah, I made it a little longer than necessary because in my bench test setup I have indicator lights in parallel with the coils for diagnostic purposes and I wanted to clearly see them light up.

That said, the device I am controlling is not a small signal relay that switches in under 20ms, but instead a rather large 12-pole “mechanically held” lighting contactor, and its transition from opened to closed or vice versa isn’t purely governed by the magnetic field. I couldn’t find any data from its manufacturer about its transition time and I haven’t had a chance to determine it experimentally, but there isn’t any harm going with an extra long pulse, because it has a built-in switch that interrupts the coil as soon as the transition has completed.

I appreciate your attempt to help me and I hope I haven’t offended you. Per the topic title, I am specifically looking to expose my Template Switch as a “light” entity. If you look at my original post for the configuration I’m currently using, it’s a little more complicated than controlling a light using a single relay. Replacing a Switch with a Binary Light and and GPIO Output could be a solution to some use cases, but not for controlling devices like the latching contactor in this example photo, which are turned on by pulsing one relay, turned off by pulsing a second relay, and need to be monitored for manually/externally triggered state changes.

I don’t understand where the problem is if you have double coil contactor.
Did I miss something?

light:
  - platform: binary
    name: "My Light"
    id: mylight
    output: dummy_output
    on_turn_on:
      - output.turn_on: output1
      - delay: 200ms
      - output.turn_off: output1

    on_turn_off:
      - output.turn_on:: output2
      - delay: 200ms
      - output.turn_off: output2


output:
  - platform: gpio
    pin: GPIOXX # some unused gpio
    id: dummy_output
  - platform: gpio
    pin: GPIO13
    id: output1
  - platform: gpio
    pin: GPIO12
    id: output2

binary_sensor:
  #Input 1 (used to monitor the contactor open/closed state):
  - platform: gpio
    id: "input1"
    name: "Input 1"
    pin: GPIO5
    #debounce:
    filters:
      - delayed_on_off: 50ms
    disabled_by_default: true
    internal: true
    on_press:
      then:
        - if:
            condition: 
              light.is_off: mylight
            then:
              - light.turn_on: mylight
    on_release:
      then:
        - if:
            condition: 
              light.is_on: mylight
            then:
              - light.turn_off: mylight

Edit: there’s no way I can make code tags to format this correctly…

No offense taken, the critical piece of information was missing from your original post. This happens frequently. Since this is mostly about HA, I likely won’t be able to help.