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
Full YAML you can try here:
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
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).
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)…
LATEST VERSION OF THE FULL YAML
UPDATED: 05-JUL-2022
For information on changes please refer to the posts in this thread.:
Deprecated YAML - Expand section to see it
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
.
.
.
.
LATEST VERSION OF THE FULL YAML
UPDATED: 07-MAR-2024
For information on changes please refer to this post in this thread.:
Deprecated YAML - Expand section to see it
template-s31-outlet.yaml
Note: placed in same folder as all other ESPHome YAML files, although there is a way to hide it, I prefer not to.
# Basic Config
esphome:
name: ${devicename}
comment: ${device_description}
friendly_name: ${friendly_devicename}
esp8266:
board: esp01_1m
early_pin_init: false #This prevents the on-off-on of the relay upon firmware upload
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
# Sync time with Home Assistant
# Used to calculate total daily energy
time:
- platform: homeassistant
id: ha_time
# Device Specific Config
uart:
rx_pin: RX
baud_rate: 4800
text_sensor:
- platform: wifi_info
ip_address:
name: "IP"
icon: "mdi:ip-outline"
update_interval: ${update_interval_wifi}
ssid:
name: "SSID"
icon: "mdi:wifi-settings"
update_interval: ${update_interval_wifi}
bssid:
name: "BSSID"
icon: "mdi:wifi-settings"
update_interval: ${update_interval_wifi}
mac_address:
name: "MAC"
icon: "mdi:network-outline"
scan_results:
name: "Wifi Scan"
icon: "mdi:wifi-refresh"
update_interval: ${update_interval_wifi}
disabled_by_default: true
binary_sensor:
#Status Binary Sensor exposes the node state (if it’s connected to via MQTT/native API) for Home Assistant.
- platform: status
name: "Connection Status"
id: connection_status
entity_category: diagnostic
- platform: gpio
id: outlet_button
name: "Button"
pin:
number: GPIO0
mode: INPUT_PULLUP
inverted: True
entity_category: ''
on_press:
then:
- if:
condition: # only toggle relay if button is enabled
lambda: 'return (id(select_button).state == "Enabled");'
then:
switch.toggle: relay
- platform: template
id: threshold_status
name: "Above Threshold"
sensor:
- platform: wifi_signal
name: "WiFi Signal"
update_interval: ${update_interval_wifi}
#https://esphome.io/components/sensor/cse7766.html
- platform: cse7766
current:
name: "Current"
id: current
state_class: measurement
device_class: current
unit_of_measurement: A
accuracy_decimals: 1
filters:
- throttle: ${cse7766_throttle_interval}
voltage:
name: "Voltage"
id: voltage
state_class: measurement
device_class: voltage
unit_of_measurement: V
accuracy_decimals: 1
filters:
- throttle: ${cse7766_throttle_interval}
- offset: ${voltage_cal}
power:
name: "Power"
id: power
state_class: measurement
device_class: power
unit_of_measurement: W
accuracy_decimals: 1
filters:
- throttle: ${cse7766_throttle_interval}
on_value: # set or clear threshold_status template binary sensor depending on whether power usage is over threshold
- if:
condition:
lambda: 'return (x >= id(power_threshold).state);'
then:
- binary_sensor.template.publish:
id: threshold_status
state: ON
- if: # set or clear threshold_status template binary sensor depending on whether power usage is above threshold
condition:
lambda: 'return (id(select_led).state == "Threshold");'
then:
- switch.turn_on: blue_led
else:
- binary_sensor.template.publish:
id: threshold_status
state: OFF
- if: # set or clear threshold_status template binary sensor depending on whether power usage is above threshold
condition:
lambda: 'return (id(select_led).state == "Threshold");'
then:
- switch.turn_off: blue_led
- platform: total_daily_energy
name: "Daily Energy"
power_id: 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
- platform: template
name: "Power Factor"
id: power_factor
#lambda: return id(power).state / id(voltage).state / id(current).state;
lambda: |-
auto x = id(power).state / id(voltage).state / id(current).state;
if (x >= 0) return x;
return 0.0;
number: # used as a threshold for whether the plugged-in devices is running.
- platform: template
name: "Power Threshold"
min_value: 1
max_value: 1800
step: 1
initial_value: 3
id: power_threshold
entity_category: config
optimistic: true # required for changing value from home assistant
restore_value: true
unit_of_measurement: W
mode: box
on_value:
- if:
condition:
lambda: 'return (id(power).state >= x);'
then:
- binary_sensor.template.publish:
id: threshold_status
state: ON
- if:
condition:
lambda: 'return (id(select_led).state == "Threshold");'
then:
- switch.turn_on: blue_led
else:
- binary_sensor.template.publish:
id: threshold_status
state: OFF
- if:
condition:
lambda: 'return (id(select_led).state == "Threshold");'
then:
- switch.turn_off: blue_led
switch:
# blue LED follows relay power state
- platform: gpio
id: blue_led
pin:
number: GPIO13
inverted: true
# relay output
- platform: gpio
id: relay
name: ""
icon: "mdi:power-socket-us"
pin: GPIO12
entity_category: ''
restore_mode: ${restore_mode_setting}
# automatically make blue led equal relay state if set to "Relay"
on_turn_on:
- if:
condition: # only if blue LED set to "Relay"
lambda: 'return (id(select_led).state == "Relay");'
then:
switch.turn_on: blue_led
on_turn_off:
- if:
condition: # only if blue LED set to "Relay"
lambda: 'return (id(select_led).state == "Relay");'
then:
switch.turn_off: blue_led
#Do not enable otherwise Blue led can't be configured
#status_led:
# pin:
# number: GPIO13
button:
- platform: restart
id: restart_button
name: "Restart"
entity_category: diagnostic
select:
# option to disable button
- platform: template
name: "Button"
id: select_button
optimistic: true
options:
- Enabled
- Disabled
initial_option: Enabled
restore_value: true
icon: mdi:circle-double
entity_category: config
# option to disable blue LED
- platform: template
name: Blue LED
id: select_led
optimistic: true
entity_category: config
options:
- Relay
- Threshold
- Disabled
initial_option: Relay
restore_value: true
icon: mdi:led-on
on_value:
then:
- if:
condition:
lambda: 'return ( (id(select_led).state == "Relay") && id(relay).state );'
then:
switch.turn_on: blue_led
else:
switch.turn_off: blue_led
Device specific YAML:
office-equipment-outlet.yaml
substitutions:
devicename: "office-equipment-outlet"
devicename_no_dashes: s31_outlet_1
friendly_devicename: "Office Equipment Outlet"
device_description: "Office Equipment Outlet - s31_outlet_1"
voltage_cal: "0.5"
restore_mode_setting: ALWAYS_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 if not possible to restore.
#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.
# Interval of how often the wifi info is updated
update_interval_wifi: "60s"
cse7766_throttle_interval: "5s"
wifi:
ssid: !secret iot_wifi_ssid
password: !secret iot_wifi_password
# Default 15min, 0s will disable. Disabling because w/HA down the relays cycle on/off every 15 minutes.
reboot_timeout: 0s
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "${devicename}"
password: !secret iot_wifi_password
#Faster than DHCP. Also use if can't reach because of name change
manual_ip:
static_ip: 10.1.3.170
gateway: 10.1.2.1
subnet: 255.255.254.0
dns1: 10.1.0.50
dns2: 10.1.0.51
#Manually override what address to use to connect to the ESP.
#Defaults to auto-generated value. Example, if you have changed your
#static IP and want to flash OTA to the previously configured IP address.
use_address: 10.1.3.170
<<: !include template-s31-outlet.yaml
.
.
.
.
LATEST VERSION OF THE FULL YAML
UPDATED: 29-JUL-2024
For the YAML and information on changes please refer to this post in this thread.: