ESPhome, Sonoff and external switches

Hi everyone,

Quick question. With Sonoff-Tasmota, it’s fairly straightforward to connect external switches, such as push button or even rocker switches to a Sonoff device and have Tasmota still work with them by connecting a cable from the switch to the GPIO(s).

Can the same be done with ESPhome? Can it still be configured so that I can control, say a light, with both a standard switch and with Home Assistant together? That is, if I turn the light off with the switch, I can reactivate it with HA via ESPhome?

You sure can

Lovely, and with the added benefit it doesn’t use MQTT so no reliance on the server if it goes down.

1 Like

I haven’t actually swapped over to the api yet. I like to be able to use a client to sub to # on my mqtt server to see what is going on when things go tits-up.

Yep lot easier, just don’t forget API way should not be used with deep sleep mode (or you’ll get longer reconnect time at HA at wake up !)

1 Like

Hello @efleming, how are you?

I have many sonoffs basics running EspHome with external switches. I’ve used GPIO14, TX and TX to attach three different physical switches with single, double, triple, quadruple, many and long presses modes, so I can use them with automations do do whatever you want with those click types. If you use it with three switches, you will have 17 possible functions, excluding the first switch, single press that is attached to the main relay. There is also a text sensor to publish the last switch number and click type so it can be used as automation triggers.

Here is the code I’ve created:

esphome:
  name: ${project_name}
  platform: ESP8266
  board: esp01_1m

wifi:
  ssid: ${wifi_ssid}
  password: ${wifi_psw}
  fast_connect: false
  ap:
    ssid: ${friendly_name}
    password: ${ap_psw}

logger:

web_server:

api:
  password: ${api_psw}

ota:

binary_sensor:

## Buton 01

  - platform: gpio
    id: button_01
    pin:
      number: GPIO14
      mode: INPUT_PULLUP
      inverted: True
    filters:
      - delayed_on: 50ms
      - delayed_off: 50ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - light.toggle: main_light
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_01_double"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_01_triple"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_01_quadruple"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_01_many"
      - timing:
          - ON for at least 2s
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_01_long"

## Buton 02

  - platform: gpio
    id: button_02
    pin:
      number: GPIO1
      mode: INPUT_PULLUP
      inverted: True
    filters:
      - delayed_on: 50ms
      - delayed_off: 50ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_02_single"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_02_double"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_02_triple"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_02_quadruple"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_02_many"
      - timing:
          - ON for at least 2s
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_02_long"

## Buton 03

  - platform: gpio
    id: button_03
    pin:
      number: GPIO3
      mode: INPUT_PULLUP
      inverted: True
    filters:
      - delayed_on: 50ms
      - delayed_off: 50ms
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_03_single"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_03_double"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_03_triple"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_03_quadruple"
      - timing:
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at most 399ms
          - ON for at most 1s
          - OFF for at least 400ms
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_03_many"
      - timing:
          - ON for at least 2s
        then:
          - text_sensor.template.publish:
              id: buttons
              state: "button_pressed"
          - delay: 50ms
          - text_sensor.template.publish:
              id: buttons
              state: "button_03_long"

output:
  - platform: gpio
    pin: GPIO12
    id: main_relay

light:
  - platform: binary
    name: ${light_entity_id}
    id: main_light
    output: main_relay

sensor:
  - platform: wifi_signal
    id: wifi
    update_interval: 60s

  - platform: template
    name: ${friendly_name} - Wifi signal
    unit_of_measurement: '%'
    accuracy_decimals: 0
    icon: "mdi:wifi"
    lambda: |-
        if (id(wifi).state < -92.0)
          return 100.0;
        if (id(wifi).state > -21.0)
          return 1.0;
        else
          return round(( -0.0154 * id(wifi).state * id(wifi).state ) - ( 0.3794 * id(wifi).state ) + 98.182 );

text_sensor:
  - platform: template
    name: ${friendly_name} - Switches
    id: buttons
    icon: "mdi:light-switch"

status_led:
  pin:
    number: GPIO13
    inverted: true

There is also a nice formula to calculate the current wifi signal quality using percentage.

Hope it helps!

9 Likes

That’s really, really cool. Thanks for posting that, I’ve got a lot to learn but that demonstrates how powerful this can be.

1 Like

Hi Guys
The codes looks really great @Schneider. I have just starting using ESPHome and look forward to testing this over the coming days. It is exactly what I have been looking for.
Cheers

1 Like

@Schneider. I wonder if you can assist me with a small error
I have a validated Yaml file as per your code, thank you
However I am getting this UNKNOWN error

${friendly_name} - Switches Unknown

I think it has something to do with the Text Sensor

text_sensor:
 -  platform: template
    name: ${friendly_name} - Switches
    id: buttons
    icon: "mdi:light-switch" 

Is there suppose to be a switch entity? or is it something else. This shows in “States” as
sensor.friendly_name_switches

Hope you can help
Thanks so much

Hello sir!

I am really sorry: I have not included the substitutions part. You can use this method to easily create templates and reuse parts of it.

Here it is:

substitutions:
  project_name: '' #underscore #MDNS
  friendly_name: ''
  device_id: '' #identification of device
  switch_entity_id: ''
  switch_icon: '' #mdi:ICON https://materialdesignicons.com/
  light_entity_id: ''
  wifi_ssid: ''
  wifi_psw: ''
  ap_psw: ''
  api_psw: '' #at least 8 characters long

These entries are used on the code I’ve sent: it will search for ${project_name} and substitute for what you have set on project_name (between the single quotes), so just substitute the ones you need. Some of these from the above are not used on this specific template shared but I use it on others that have, for example, switches. You can leave it blank.

Try it and send a validate now whit this substitutions above everything, on the same yaml as your code.

Let me know if you need any assistance.

Have a great day!

Thank you so much kind sir. I shall try this a little later and let you know how I get on. Thanks @Schneider

Kind Regards

1 Like

Hi again @Schneider
Well I have loaded the additional comments from the substitutions and unfortunately have not had great success
I added project_name, friendly_name, device_id, switch_entity_id, and light_entity_id
I left out wifi, ap, and api as I have my own wifi config setup
Sadly after running OTA update the sonoff would not reconnect to wifi. I then had to recompile the binary and use USB to re-flash the sonoff as OTA was not available due to not reconnecting to wifi.
I removed one line at a time to see which line was potentially causing, recompile the bin file, and re-flash each time.
Unfortunately it was not until I had remove every line from the substitution list, including substitution: that I finally got a bin file that allowed the sonoff to reconnect to wifi
So basically taking the yaml file back to the original format that I had before posting the previous error message to you
I am now at a loss as to why the upload was causing the sonoff not to reconnect to wifi???
I can only imagine it is something simple (or hopefully something simple) that is causing this issue

I wonder if I could post my yaml file somewhere for you to review?
Also of note
I am only using 2 buttons connected to GPIO14 and GIPO3
Button 1 connected to GPIO14 - No response
Button 2 connected to GPIO3 - working and getting on/off response in log when press and release the button.
Will check Button 1 config shortly

Hope you can over some comments.
Thanks again

Hello again!

Sorry to hear that.

No worries, you can send over private message here and I will review it for you.

Thanks.

Hi
I have a similar issue with external switches.

My setup has a Sonoff Dual R2 controlling a dumb window shutter motor. Also, there is a 3-position rocker switch connected to Button0 (GPIO9) and Button1 (GPIO0) on the 4-pin header in the Sonoff.

I have setup ESPHome for a time-based cover. It works well via HA. The button on the Sonoff also works open-stop-close-stop.

Adding control via the rocker switch is proving difficult as I do not know what to set for ‘mode’ & ‘inverted’. I have tried the following:

binary_sensor:
- platform: gpio
  pin:
    number: GPIO0
  id: open_button
  on_press:
    then:
      - switch.toggle: open_cover

- platform: gpio
  pin:
    number: GPIO9
  id: close_button
  on_press:
    then:
      - switch.toggle: close_cover

- platform: gpio
  pin:
    number: GPIO10
    inverted: true
  id: button
  on_press:
    then:
      # logic for cycling through movements: open->stop->close->stop->...
      - lambda: |
         if (id($device_name).current_operation == COVER_OPERATION_IDLE) {
           // Cover is idle, check current state and either open or close cover.
           if (id(cover_going_up) || id($device_name).is_fully_closed()) {
             id($device_name).open();
             id(cover_going_up) = false;
           } else {
             id($device_name).close();
             id(cover_going_up) = true;
           }
         } else {
           // Cover is opening/closing. Stop it.
           id($device_name).stop();
         }

switch:
- platform: gpio
  pin: GPIO12
  interlock: &interlock [open_cover, close_cover]
  id: open_cover
- platform: gpio
  pin: GPIO5
  interlock: *interlock
  id: close_cover
  
cover:
- platform: time_based
  name: $upper_device_name
  id: $device_name
  open_action:
    - switch.turn_on: open_cover
  open_duration: 16s

  close_action:
    - switch.turn_on: close_cover
  close_duration: 16s

  stop_action:
    - switch.turn_off: open_cover
    - switch.turn_off: close_cover

Any insight would be most appreciated.

Thank you.

Good night,

I already have a basic configuration for controlling a relay connected to a NODEMCU, and it works very well in Hassio, however, I have picked up enough to implement a physical switch (pulsator) in this context and to be able to use both options in harmony. I have a push button attached to pin D2 and the Nodemcu GND, but it never recognizes the touch of the switch and turns the relay on or off. Can anyone help me?

Thank you very much