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

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:
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:
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