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
 
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.
 
      
  
        
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:  - 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 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:  - platform: esphome    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'