devices.esphome.io

Sonoff M5 Wall Switch 1/2/3-gang

Sonoff M5 Wall Switch 1/2/3-gang

Device Type: switch
Electrical Standard: eu
Board: esp32
Difficulty: Disassembly required, 3/5

Notes

  • the Matter compatible version of this switch (part numbers ending in W, e.g. M5-2C-86W) is locked and cannot be flashed
  • status LED (blue) in left-most button
  • channel LEDs (red) are dimmable (PWM) while relays OFF; 100% bright when ON
  • in 1-gang version LED 1 to/can be activated separately from Relay
  • in 2-gang version LED 2 to/can be activated separately from Relay

Troubleshooting

  • FTDI adapters typically provide enough power to flash ESPHome onto these devices, but due to the PCB design, they do not provide sufficient power to boot. This will typically present as esphome rst:0x1 (POWERON_RESET), boot:0x13 (SPI_FAST_FLASH_BOOT). When connected to and powered by its wall plate, it will boot normally.

header

GPIO Pinout

1-Gang Version

PinFunction
GPIO00Button 1
GPIO23Relay 1
GPIO19LED 1
GPIO05Status LED
GPIO18PWM for LED 1

2-Gang Version

PinFunction
GPIO04Button 1
GPIO15Button 2
GPIO23Relay 1 / LED 1
GPIO19Relay 2
GPIO22LED 2
GPIO05Status LED
GPIO18PWM for LED 1/2

3-Gang Version

PinFunction
GPIO04Button 1
GPIO00Button 2
GPIO15Button 3
GPIO23Relay 1 / LED 1
GPIO19Relay 2 / LED 2
GPIO22Relay 3 / LED 3
GPIO05Status LED
GPIO18PWM for LED 1/2/3

Basic Configuration (2-Gang)

esphome:
name: Sonoff M5 2gang
esp32:
board: esp32dev
framework:
type: arduino
logger:
level: INFO
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
encryption:
key: !secret esp_api_key
ota:
- platform: esphome
password: !secret ota_secret
sensor:
- platform: wifi_signal
name: "RSSI"
id: sensor_rssi
update_interval: 90s
entity_category: "diagnostic"
- platform: uptime
name: "Uptime"
id: sensor_uptime
update_interval: 300s
entity_category: "diagnostic"
button:
- platform: restart
name: "Restart"
id: button_restart
switch:
- platform: gpio
name: "Left"
pin: GPIO23
id: relay_1
- platform: gpio
name: "Right"
pin: GPIO19
id: relay_2
on_turn_on:
- output.turn_on: led_2
on_turn_off:
- output.turn_off: led_2
output:
- platform: gpio
id: led_2
pin:
number: GPIO22
inverted: False
- platform: ledc
id: pwm_output
pin: GPIO18
frequency: 1000 Hz
binary_sensor:
- platform: status
name: "Status"
id: sensor_status
- platform: template
name: "API connected"
id: sensor_api_connected
internal: True
entity_category: 'diagnostic'
device_class: 'connectivity'
lambda: return global_api_server->is_connected();
on_press:
- light.turn_on: led_status
on_release:
- light.turn_off: led_status
- platform: gpio
name: "Left"
pin:
number: GPIO04
mode: INPUT_PULLUP
inverted: False
on_press:
- switch.toggle: relay_1
- platform: gpio
name: "Right"
pin:
number: GPIO15
mode: INPUT_PULLUP
inverted: False
on_press:
- switch.toggle: relay_2
light:
- platform: status_led
name: "LED"
id: led_status
pin:
number: GPIO05
inverted: True
internal: True
restore_mode: ALWAYS_OFF
- platform: monochromatic
output: pwm_output
name: "LEDs"
restore_mode: RESTORE_DEFAULT_OFF
icon: 'mdi:led-outline'
entity_category: 'config'

Advanced Configuration (3-Gang, US Version)

# ESPHome Firmware
# Sonoff Switchman M5 3-Gang US
# Copyright (c) 2024 Mario Di Vece
# License: [MIT](https://opensource.org/license/mit/)
# Decription:
# Aims to provide a feature-rich, production-ready firmware for this elegant device
# - Provides Diagnostic data plus, status LED indicator when not connected to Home Assistant API
# - Relays may be configured individually on the UI to work in decoupled mode.
# LEDs are physically connected to the relays, and they can't be individually controlled :(
# - Exposes gestures via events:
# esphome.on_gesture { button: (A|B|C), gesture: (click|double_click|hold) }
# - Off-state (Background Brightness) of the LEDs is configurable via the UI
#
# For the below example, you need to keep the following entries in your secrets.yaml file:
# - wifi_ssid: "<secret>"
# - wifi_password: "<secret>"
# - ota_password: "<secret>"
# - esp_key: "<32-byte-base-64-secret>"
#
# Example file (sonoff-m5-3g-office-01.yaml)
#
# substitutions:
# usemac: "false"
# friendly: "Sonoff M5 3G - Office Switch - 01"
# uniquename: "sonoff-m5-3g-office-01"
#
# packages:
# base_package:
# url: https://github.com/mariodivece/esphometemplates/
# ref: main
# files: [sonoff-m5-3g-us.yaml]
# refresh: 0d
#
# wifi:
# use_address: "10.16.40.49"
# Basic substitutions (can be safely overriden)
substitutions:
usemac: "true"
friendly: "Sonoff Switchman M5 3G US"
uniquename: "switch-m5-3g"
loglevel: INFO
apikey: !secret esp_key
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
ota_password: !secret ota_password
device_name: "M53G"
device_make: "Sonoff"
sw_version: "2024.2.4"
package_url: "github://mariodivece/esphometemplates/sonoff-m5-3g-us.yaml@main"
# Define the board for the compiler
esp32:
board: esp32dev
framework:
type: arduino
# Setup the integration and define some project variables
esphome:
name: "${uniquename}"
friendly_name: "${friendly}"
comment: "${device_name} by ${device_make}"
name_add_mac_suffix: ${usemac}
min_version: "2023.2.0"
project:
name: "${device_make}.${device_name}"
version: "${sw_version}"
# Allow importing this package
dashboard_import:
package_import_url: ${package_url}
import_full_config: false
# Enable logging
logger:
level: "${loglevel}"
# Enable Home Assistant API
api:
encryption:
key: "${apikey}"
# Enable OTA
ota:
- platform: esphome
safe_mode: true
password: !secret ota_password
# Enable WiFi and AP for captive portal
wifi:
fast_connect: false
power_save_mode: none
ssid: "${wifi_ssid}"
password: "${wifi_password}"
# Enable fallback hotspot (captive portal) in case wifi connection fails
# password for hostspot is the same as password for net AP (needs captive_portal)
ap:
ssid: "${uniquename}-setup"
password: "${wifi_password}"
captive_portal:
# Diagnostic output sensors
text_sensor:
- platform: template
name: "Deployment Version"
lambda: return {"${sw_version}"};
icon: "mdi:tag"
entity_category: diagnostic
- platform: wifi_info
ip_address:
id: ip_address
name: "IP Address"
icon: "mdi:wan"
sensor:
- platform: template
id: internal_temp
name: "Internal Temperature"
icon: "mdi:thermometer"
unit_of_measurement: "°C"
entity_category: diagnostic
disabled_by_default: true
lambda: return temperatureRead();
- platform: wifi_signal
name: "RSSI"
id: sensor_rssi
update_interval: 90s
entity_category: "diagnostic"
- platform: uptime
name: "Uptime"
id: sensor_uptime
update_interval: 300s
entity_category: "diagnostic"
# Provide a pre-built button for restarting the device
button:
- platform: restart
name: "Restart"
id: button_restart
switch:
# Physical GPIO Relay
- platform: gpio
name: "Relay A"
pin: GPIO23
id: relay_a
# Physical GPIO Relay
- platform: gpio
name: "Relay B"
pin: GPIO19
id: relay_b
# Physical GPIO Relay
- platform: gpio
name: "Relay C"
pin: GPIO22
id: relay_c
# Config-only switch to decouple relay from button
- platform: template
name: "Decoupling - Relay A"
id: relay_a_decoupled
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
icon: 'mdi:link-box-outline'
entity_category: 'config'
# Config-only switch to decouple relay from button
- platform: template
name: "Decoupling - Relay B"
id: relay_b_decoupled
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
icon: 'mdi:link-box-outline'
entity_category: 'config'
# Config-only switch to decouple relay from button
- platform: template
name: "Decoupling - Relay C"
id: relay_c_decoupled
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
icon: 'mdi:link-box-outline'
entity_category: 'config'
output:
# Physical GPIO PWM for off-state background brightness
# This pin controls the background brightness for all LEDs
# physically attached to the relays
- platform: ledc
id: pwm_output
pin: GPIO18
frequency: 1000 Hz
binary_sensor:
# Diagnostic sensor for connection
- platform: status
name: "Status"
id: sensor_status
# Make the status LED blink when not connected/trying to connect
- platform: template
name: "API connected"
id: sensor_api_connected
internal: true
entity_category: 'diagnostic'
device_class: 'connectivity'
lambda: return global_api_server->is_connected();
on_press:
- light.turn_off: led_status
on_release:
- light.turn_on: led_status
# Physical Button A
- platform: gpio
name: "Button A"
id: button_a
pin:
number: GPIO04
mode: INPUT_PULLUP
inverted: true
filters:
- delayed_on: 10ms
on_press:
- if:
condition:
switch.is_off: relay_a_decoupled
then:
- switch.toggle: relay_a
on_multi_click:
# single click detection
- timing:
- ON for at most 900ms
- OFF for at least 600ms
then:
- homeassistant.event:
event: esphome.on_gesture
data:
button: "A"
gesture: "single_click"
# double click detection
- timing:
- ON for at most 500ms
- OFF for at most 400ms
- ON for at most 500ms
- OFF for at least 250ms
then:
- homeassistant.event:
event: esphome.on_gesture
data:
button: "A"
gesture: "double_click"
# hold detection
- timing:
- ON for at least 1s
then:
- while:
condition:
binary_sensor.is_on: button_a
then:
- light.toggle: led_status
- homeassistant.event:
event: esphome.on_gesture
data:
button: "A"
gesture: "button_hold"
- delay: 100ms
- light.turn_off: led_status
- platform: gpio
name: "Button B"
id: button_b
pin:
number: GPIO00
mode: INPUT_PULLUP
inverted: true
filters:
- delayed_on: 10ms
on_press:
- if:
condition:
switch.is_off: relay_b_decoupled
then:
- switch.toggle: relay_b
on_multi_click:
# single click detection
- timing:
- ON for at most 900ms
- OFF for at least 600ms
then:
- homeassistant.event:
event: esphome.on_gesture
data:
button: "B"
gesture: "single_click"
# double click detection
- timing:
- ON for at most 500ms
- OFF for at most 400ms
- ON for at most 500ms
- OFF for at least 250ms
then:
- homeassistant.event:
event: esphome.on_gesture
data:
button: "B"
gesture: "double_click"
# hold detection
- timing:
- ON for at least 1s
then:
- while:
condition:
binary_sensor.is_on: button_b
then:
- light.toggle: led_status
- homeassistant.event:
event: esphome.on_gesture
data:
button: "B"
gesture: "button_hold"
- delay: 100ms
- light.turn_off: led_status
- platform: gpio
name: "Button C"
id: button_c
pin:
number: GPIO15
mode: INPUT_PULLUP
inverted: true
filters:
- delayed_on: 10ms
on_press:
- if:
condition:
switch.is_off: relay_c_decoupled
then:
- switch.toggle: relay_c
on_multi_click:
# single click detection
- timing:
- ON for at most 900ms
- OFF for at least 600ms
then:
- homeassistant.event:
event: esphome.on_gesture
data:
button: "C"
gesture: "single_click"
# double click detection
- timing:
- ON for at most 500ms
- OFF for at most 400ms
- ON for at most 500ms
- OFF for at least 250ms
then:
- homeassistant.event:
event: esphome.on_gesture
data:
button: "C"
gesture: "double_click"
# hold detection
- timing:
- ON for at least 1s
then:
- while:
condition:
binary_sensor.is_on: button_c
then:
- light.toggle: led_status
- homeassistant.event:
event: esphome.on_gesture
data:
button: "C"
gesture: "button_hold"
- delay: 100ms
- light.turn_off: led_status
light:
# Physical pin to the connection status LED
# We don't expose this to the HA UI (internal)
- platform: status_led
name: "LED"
id: led_status
pin:
number: GPIO05
inverted: true
internal: true
restore_mode: RESTORE_DEFAULT_ON
# HA UI connection to the background brightness (PWM) pin
- platform: monochromatic
output: pwm_output
name: "Background Brightness"
restore_mode: RESTORE_DEFAULT_OFF
icon: 'mdi:led-outline'
entity_category: 'config'
Edit this page on GitHub