devices.esphome.io
Sonoff M5 Wall Switch 1/2/3-gang
Sonoff M5 Wall Switch 1/2/3-gang
Device Type: switchElectrical Standard: euBoard: esp32Difficulty: 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
GPIO Pinout
1-Gang Version
Pin | Function |
---|---|
GPIO00 | Button 1 |
GPIO23 | Relay 1 |
GPIO19 | LED 1 |
GPIO05 | Status LED |
GPIO18 | PWM for LED 1 |
2-Gang Version
Pin | Function |
---|---|
GPIO04 | Button 1 |
GPIO15 | Button 2 |
GPIO23 | Relay 1 / LED 1 |
GPIO19 | Relay 2 |
GPIO22 | LED 2 |
GPIO05 | Status LED |
GPIO18 | PWM for LED 1/2 |
3-Gang Version
Pin | Function |
---|---|
GPIO04 | Button 1 |
GPIO00 | Button 2 |
GPIO15 | Button 3 |
GPIO23 | Relay 1 / LED 1 |
GPIO19 | Relay 2 / LED 2 |
GPIO22 | Relay 3 / LED 3 |
GPIO05 | Status LED |
GPIO18 | PWM 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 compileresp32: board: esp32dev framework: type: arduino
# Setup the integration and define some project variablesesphome: 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 packagedashboard_import: package_import_url: ${package_url} import_full_config: false
# Enable logginglogger: level: "${loglevel}"
# Enable Home Assistant APIapi: encryption: key: "${apikey}"
# Enable OTAota: safe_mode: true password: !secret ota_password
# Enable WiFi and AP for captive portalwifi: 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 sensorstext_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 devicebutton: - 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'