Sonoff S31 Outlet with ESPHome - Example YAML

EDIT: The YAML has changed over time as I make improvements and corrections. I am not rewriting this post as for the most part it is still correct (if not let me know) but keep in mind the YAML at the bottom of the post has evolved a bit compared to what you see in this first post.

I found a basic version of the code below for the Sonoff S31 and made a number of improvements, at least for my use scenario. I rely a lot on people sharing so I wanted to share back in case it helps others. I had started off with Tasmota and really liked the fact the outlet had a web interface and how much information was available for each device. What I did not like was the complexity of setting it all up due to MQTT being in the middle. It was not difficult but it felt unnecessarily complicated when ESPHome has a more direct integration. Anyway this thread is not meant o be a debate between ESPHome and Tasmota so back to the YAML:

This is the entire code needed:

substitutions:
  devicename: s31-outlet-1
  friendly_devicename: "Office Equipment Outlet"
  
# Basic Config
esphome:
  name: ${devicename}
  platform: ESP8266
  board: esp01_1m

The substitutions section allows to define variables so that you don’t have to edit the YAML in many places each time you create a new device using this YAML or simply make a change.

The devicename needs to be short and a unique identifier. I would set it, and leave it, named as you named the configuration in ESPHome when you created the device. For some reason I run into issues (device not found, etc.) if I mess with it. This is also how this device will be named on the Integration page under ESPHome (but you can rename it there if you really want to), and will be the default device name in Home Assistant (which we will change).

The friendly_devicename is what you will call the device. This device is an outlet so I would recommend naming it with the name of the device attached to the outlet, or blah blah outlet in the event the outlet is powering has multiple devices. This is important as it will determine what all the other entities will be named and will save you lots of entity editing later.

wifi:
  ssid: !secret iot_wifi_ssid
  password: !secret iot_wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${devicename} Hotspot"
    password: !secret iot_wifi_password

When you see !secret it is referring to a variable in your secrets.yaml file which can be edited directly from within ESPHome (top right of window). Just add the following editing in your details:

# Your IOT Wi-Fi SSID and password
iot_wifi_ssid: "IoT_ SSID_ here"
iot_wifi_password: "password_here"

I really wanted to see some device information when loading up the IP of the device in a web browser like Tasmota. While not as fancy, a lot can be shown on ESPDevices just by adding a few lines of YAML. I don’t know what include_internal: true actually does as I don’t believe I saw a difference with or without it, but it doesn’t seem to be causing issues so I left it.

web_server:
  port: 80
  include_internal: true

Whether needed or not, I wanted to see all connectivity variables as entities for easier troubleshooting. When you start having lots of devices this is handy. Here we see the ${friendly_devicename} variable which will give the entities a name that includes the device name.

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${friendly_devicename}: IP"
      icon: "mdi:ip-outline"
    ssid:
      name: "${friendly_devicename}: SSID"
      icon: "mdi:wifi-settings"
    bssid:
      name: "${friendly_devicename}: BSSID"
      icon: "mdi:wifi-settings"
    mac_address:
      name: "${friendly_devicename}: MAC"
      icon: "mdi:network-outline"
    scan_results:
      name: "${friendly_devicename}: Wifi Scan"
      icon: "mdi:wifi-refresh"
      disabled_by_default: true

This is how it will look in HA:

In the example above, only IP will appear on the device page but on a dashboard it will sow as Office Equipment Outlet: IP.

Same thing for the Controls and Sensors of the device. In the sensor section I changed the Energy over time from W (which I believe was showing with 3 decimals which was a bit silly) to kW with 3 decimals. I would argue that seeing anything under 1W is really meaningless and probably not even very accurate for this kind of device. If you set it to kW the numbers will be smaller and you will still see down to the 1W granularity. For those wanting to figure out the cost of the power used, this makes it a tiny bit simpler since power is sold by the kWh. Before this change, 2.345kW was showing as 2,345.678W. I would argue that the 1 decimal accuracy for voltage and power are also pointless but I left them since almost all other devices show that or more.

sensor:
  - platform: wifi_signal
    name: "${friendly_devicename}: WiFi Signal"
    update_interval: 60s
  - platform: cse7766
    current:
      name: "${friendly_devicename}: Current"
      accuracy_decimals: 1
    voltage:
      name: "${friendly_devicename}: Voltage"
      accuracy_decimals: 1
    power:
      name: "${friendly_devicename}: Power"
      accuracy_decimals: 1
      id: my_power
  - platform: total_daily_energy
    name: "${friendly_devicename}: Daily Energy"
    power_id: my_power
    filters:
      # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
    unit_of_measurement: kW

No changes here, but I believe the restore_mode: ALWAYS_ON is what makes the outlet turn on immediately after being powered up.

switch:
  - platform: gpio
    name: "${friendly_devicename}"
    icon: "mdi:power-socket-us"
    pin: GPIO12
    id: relay
    restore_mode: ALWAYS_ON

If this doesn’t work for you, the other options should be here:

I have not tested these yet, but the page above lists these as options:

* **restore_mode** (*Optional*): Control how the GPIO Switch attempts to restore state on bootup. For restoring on ESP8266s, also see `restore_from_flash` in the [esp8266 section](https://esphome.io/components/esp8266.html).
> * `RESTORE_DEFAULT_OFF` (Default) - Attempt to restore state and default to OFF if not possible to restore.
>   * `RESTORE_DEFAULT_ON` - Attempt to restore state and default to ON.
>   * `RESTORE_INVERTED_DEFAULT_OFF` - Attempt to restore state inverted from the previous state and default to OFF.
>   * `RESTORE_INVERTED_DEFAULT_ON` - Attempt to restore state inverted from the previous state and default to ON.
>   * `ALWAYS_OFF` - Always initialize the pin as OFF on bootup.
>   * `ALWAYS_ON` - Always initialize the pin as ON on bootup.

I read this part is needed if you want the energy measurement kWh to be reset daily. In most cases you either need a time/date from the start of the measurement, or just reset it daily. I would like a start time/date as Tasmota offered and a way to reset it as needed. I’ll research this to see if it is possible from within the device YAML itself.

time:
  - platform: sntp
    id: my_time

:point_right::point_right::point_right: Full YAML you can try here: :point_left::point_left::point_left:

:point_right: Edit: see further down for updated full YAML

substitutions:
  devicename: s31-outlet-1
  friendly_devicename: "Office Equipment Outlet"
  
# Basic Config
esphome:
  name: ${devicename}
  platform: ESP8266
  board: esp01_1m

wifi:
  ssid: !secret iot_wifi_ssid
  password: !secret iot_wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${devicename} Hotspot"
    password: !secret iot_wifi_password

logger:
  baud_rate: 0 # (UART logging interferes with cse7766)
  
# Remove this line if you're not using Home Assistsant or your switch will restart every now and again
api:

ota:

web_server:
  port: 80
  include_internal: true

# Device Specific Config

uart:
  rx_pin: RX
  baud_rate: 4800

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${friendly_devicename}: IP"
      icon: "mdi:ip-outline"
    ssid:
      name: "${friendly_devicename}: SSID"
      icon: "mdi:wifi-settings"
    bssid:
      name: "${friendly_devicename}: BSSID"
      icon: "mdi:wifi-settings"
    mac_address:
      name: "${friendly_devicename}: MAC"
      icon: "mdi:network-outline"
    scan_results:
      name: "${friendly_devicename}: Wifi Scan"
      icon: "mdi:wifi-refresh"
      disabled_by_default: true

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "${friendly_devicename}: Button"
    on_press:
      - switch.toggle: relay
  - platform: status
    name: "${friendly_devicename}: Status"

sensor:
  - platform: wifi_signal
    name: "${friendly_devicename}: WiFi Signal"
    update_interval: 60s
  - platform: cse7766
    current:
      name: "${friendly_devicename}: Current"
      accuracy_decimals: 1
    voltage:
      name: "${friendly_devicename}: Voltage"
      accuracy_decimals: 1
    power:
      name: "${friendly_devicename}: Power"
      accuracy_decimals: 1
      id: my_power
  - platform: total_daily_energy
    name: "${friendly_devicename}: Daily Energy"
    power_id: my_power
    filters:
      # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
    unit_of_measurement: kW

switch:
  - platform: gpio
    name: "${friendly_devicename}"
    icon: "mdi:power-socket-us"
    pin: GPIO12
    id: relay
    restore_mode: ALWAYS_ON

time:
  - platform: sntp
    id: my_time

status_led:
  pin: GPIO13

Thank you to all those who have helped me on HA stuff! I hope this counts as giving back a little :slight_smile:

Disclaimer: As mentioned above, I found most of this code online (sorry I forgot where) and just customized it a bit. Not claiming any authorship!

I would now add the device leaving the name that matches what it is called in ESPHome. Once added, go to the device to make one last change. There I would only change the device name on the top left to match what you used in the friendly_devicename variable. In the example above it is Office Equipment Outlet. Doing so will make your device page look like this without further editing:

Most of the time I end up having to edit every single entity for it to be named following what I adopted as my standard. With this YAML I don’t and it has become much easier to add/remove. In fact, if you end up with other entities under this device that you want to get rid of (I did while making changes), just delete it from the ESPHome Integration (only the integration!!!) and add it back (Add integration, select ESPHome, and enter IP).

image

The only entity that I set as disabled by default is the Wifi Scan one that shows all the APs it receives. It’s unnecessary in most cases… you may want it only if you have connectivity issues and want to see what it receives.

This is how it will show up in ESPHome… too bad one can’t add a descriptor.

Last, the web interface (default view)…

:point_right::point_right::point_right: LATEST VERSION OF THE FULL YAML :point_left::point_left::point_left:
UPDATED: 05-JUL-2022
For information on changes please refer to the posts in this thread.:

substitutions:
  devicename: s31-outlet-1
  devicename_no_dashes: s31_outlet_1
  friendly_devicename: "Office Equipment Outlet"
  restore_mode_setting: RESTORE_DEFAULT_ON
  #restore_mode: Control how the relay attempts to restore state on bootup.
  #RESTORE_DEFAULT_OFF          - Attempt to restore state and default to OFF if not possible to restore.
  #RESTORE_DEFAULT_ON           - Attempt to restore state and default to ON.
  #RESTORE_INVERTED_DEFAULT_OFF - Attempt to restore state inverted from the previous state and default to OFF.
  #RESTORE_INVERTED_DEFAULT_ON  - Attempt to restore state inverted from the previous state and default to ON.
  #ALWAYS_OFF                   - Always initialize the pin as OFF on bootup.
  #ALWAYS_ON                    - Always initialize the pin as ON on bootup.


# Basic Config
esphome:
  name: ${devicename}
  platform: ESP8266
  board: esp01_1m

wifi:
  ssid: !secret iot_wifi_ssid
  password: !secret iot_wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${devicename} Hotspot"
    password: !secret iot_wifi_password

logger:
  baud_rate: 0 # (UART logging interferes with cse7766)
  
# Remove this line if you're not using Home Assistsant or your switch will restart every now and again
api:

ota:

web_server:
  port: 80
  include_internal: true

# Device Specific Config

uart:
  rx_pin: RX
  baud_rate: 4800

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${friendly_devicename}: IP"
      icon: "mdi:ip-outline"
    ssid:
      name: "${friendly_devicename}: SSID"
      icon: "mdi:wifi-settings"
    bssid:
      name: "${friendly_devicename}: BSSID"
      icon: "mdi:wifi-settings"
    mac_address:
      name: "${friendly_devicename}: MAC"
      icon: "mdi:network-outline"
    scan_results:
      name: "${friendly_devicename}: Wifi Scan"
      icon: "mdi:wifi-refresh"
      disabled_by_default: true

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "${friendly_devicename}: Button"
    on_press:
      - switch.toggle: relay
  - platform: status
    name: "${friendly_devicename}: Status"

sensor:
  - platform: wifi_signal
    name: "${friendly_devicename}: WiFi Signal"
    update_interval: 60s
  - platform: cse7766
    current:
      name: "${friendly_devicename}: Current"
      state_class: measurement
      device_class: current
      unit_of_measurement: A
      accuracy_decimals: 1    
    voltage:
      name: "${friendly_devicename}: Voltage"
      state_class: measurement
      device_class: voltage
      unit_of_measurement: V
      accuracy_decimals: 1
    power:
      name: "${friendly_devicename}: Power"
      id: "${devicename_no_dashes}_power"
      state_class: measurement
      device_class: power
      unit_of_measurement: W
      accuracy_decimals: 1
  - platform: total_daily_energy
    name: "${friendly_devicename}: Daily Energy"
    power_id: "${devicename_no_dashes}_power"
    filters:
      # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
    unit_of_measurement: kW
    state_class: total_increasing
    device_class: energy
    # 3 shows each W consumed, 2 every 10W, 1 every 100W
    accuracy_decimals: 1

switch:
  - platform: gpio
    name: "${friendly_devicename}"
    icon: "mdi:power-socket-us"
    pin: GPIO12
    id: relay
    restore_mode: ${restore_mode_setting}

time:
  - platform: sntp
    id: my_time

status_led:
  pin: GPIO13
4 Likes

Thanks for setting this up @aruffell. I just got a few of these outlets and I used this template to get them quickly into HA. I had some trouble getting it recognized by the Energy dashboard so I made some edits to the sensor section. All is good now. If you find it helpful feel free to include them in your template (I changed devicename substitutions variable so it would require an edit to the below).

sensor:
  - platform: wifi_signal
    name: "${friendly_devicename}: WiFi Signal"
    update_interval: 60s
  - platform: cse7766
    current:
      name: "${friendly_devicename}: Current"
      entity_category: diagnostic
      accuracy_decimals: 1
      state_class: measurement
      unit_of_measurement: A
      device_class: current
    voltage:
      name: "${friendly_devicename}: Voltage"
      entity_category: diagnostic
      accuracy_decimals: 1
      state_class: measurement
      device_class: voltage 
      unit_of_measurement: V
    power:
      name: "${friendly_devicename}: Power"
      entity_category: diagnostic
      accuracy_decimals: 1
      state_class: measurement
      device_class: energy  
      unit_of_measurement: W    
      id: "${s31_outlet_1}_power"
  - platform: total_daily_energy
    name: "${friendly_devicename}: Daily Energy"
    power_id: "${s31_outlet_1}_power"
    filters:
      - multiply: 0.001
    unit_of_measurement: kWh
    state_class: total_increasing
    device_class: energy
    accuracy_decimals: 1

@techanti Awesome! Thank you! I incorporated most of your edits and had a question about a couple.

I see that you declared Current, Voltage, Power as entity_category: diagnostic but I don’t believe that to be appropriate based on the definition here:

Is there a particular reason for doing so?

Were all the other…

state_class: measurement
device_class: energy  
unit_of_measurement: x

…needed for the sensor to be usable in the Energy dashboard?

I included your changes, albeit modified, for the power ids (id & power_id). Do they need to be unique thus prompting the change? My modification to what you did just pushes the edit to the top and will require just one edit.

This is what the full code looks like now:

substitutions:
  devicename: s31-outlet-1
  devicename_no_dashes: s31_outlet_1
  friendly_devicename: "Office Equipment Outlet"
  restore_mode_setting: RESTORE_DEFAULT_ON
  #restore_mode: Control how the relay attempts to restore state on bootup.
  #RESTORE_DEFAULT_OFF          - Attempt to restore state and default to OFF if not possible to restore.
  #RESTORE_DEFAULT_ON           - Attempt to restore state and default to ON.
  #RESTORE_INVERTED_DEFAULT_OFF - Attempt to restore state inverted from the previous state and default to OFF.
  #RESTORE_INVERTED_DEFAULT_ON  - Attempt to restore state inverted from the previous state and default to ON.
  #ALWAYS_OFF                   - Always initialize the pin as OFF on bootup.
  #ALWAYS_ON                    - Always initialize the pin as ON on bootup.


# Basic Config
esphome:
  name: ${devicename}
  platform: ESP8266
  board: esp01_1m

wifi:
  ssid: !secret iot_wifi_ssid
  password: !secret iot_wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${devicename} Hotspot"
    password: !secret iot_wifi_password

logger:
  baud_rate: 0 # (UART logging interferes with cse7766)
  
# Remove this line if you're not using Home Assistsant or your switch will restart every now and again
api:

ota:

web_server:
  port: 80
  include_internal: true

# Device Specific Config

uart:
  rx_pin: RX
  baud_rate: 4800

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "${friendly_devicename}: IP"
      icon: "mdi:ip-outline"
    ssid:
      name: "${friendly_devicename}: SSID"
      icon: "mdi:wifi-settings"
    bssid:
      name: "${friendly_devicename}: BSSID"
      icon: "mdi:wifi-settings"
    mac_address:
      name: "${friendly_devicename}: MAC"
      icon: "mdi:network-outline"
    scan_results:
      name: "${friendly_devicename}: Wifi Scan"
      icon: "mdi:wifi-refresh"
      disabled_by_default: true

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "${friendly_devicename}: Button"
    on_press:
      - switch.toggle: relay
  - platform: status
    name: "${friendly_devicename}: Status"

sensor:
  - platform: wifi_signal
    name: "${friendly_devicename}: WiFi Signal"
    update_interval: 60s
  - platform: cse7766
    current:
      name: "${friendly_devicename}: Current"
      state_class: measurement
      device_class: current
      unit_of_measurement: A
      accuracy_decimals: 1    
    voltage:
      name: "${friendly_devicename}: Voltage"
      state_class: measurement
      device_class: voltage
      unit_of_measurement: V
      accuracy_decimals: 1
    power:
      name: "${friendly_devicename}: Power"
      id: "${devicename_no_dashes}_power"
      state_class: measurement
      device_class: energy
      unit_of_measurement: W
      accuracy_decimals: 1
  - platform: total_daily_energy
    name: "${friendly_devicename}: Daily Energy"
    power_id: "${devicename_no_dashes}_power"
    filters:
      # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
    unit_of_measurement: kWh
    state_class: total_increasing
    device_class: energy
    # 3 shows each W consumed, 2 every 10W, 1 every 100W
    accuracy_decimals: 1

switch:
  - platform: gpio
    name: "${friendly_devicename}"
    icon: "mdi:power-socket-us"
    pin: GPIO12
    id: relay
    restore_mode: ${restore_mode_setting}

time:
  - platform: sntp
    id: my_time

status_led:
  pin: GPIO13

I have changed restore_mode: ALWAYS_ON to restore_mode: RESTORE_DEFAULT_ON as I believe it is what most people would likely prefer (and it is easy to change anyway). I also moved the setting up top to make it easier to spot and edit.

1 Like

As I understand things, The class and unit of measure have to be set for them be usable in the Energy dashboard. entity_category comes from the entity definition below and I usually have it on the esphome things I do while I working on them but often neglect to remove it:

https://developers.home-assistant.io/docs/core/entity/#generic-properties

Again, thanks of for this. It save me a lot time.

1 Like

@techanti - I found errors in the “Statistics” section stating that "The unit (“W”) of this entity doesn’t match a unit of device class ‘energy’.

I checked and found that the device_class for Power (W) should be “power” and not “energy” so I made the correction and updated the code in my first post (at the bottom).

1 Like