Shelly 2 dimmer

After thoroughly searching this forum and google. I have to ask.

Can I easiliy flash a shelly 2 dimmer with esphome?

Preferably with:

  1. OTA
  2. A valid yaml example

There doesn’t seem to be a valid firmware yet, if you follow this: https://github.com/arendst/Tasmota/issues/6914

I assume, ESPhome has the same problem.

Hmz… anyone aware of any comparable wifi builtin dimmer flash able with esphome?

Please :slight_smile: there must be something

Why can’t you stick with shelly FW?

I hate cloud

1 Like

Me too.
All Shelly devices can be configured to work without cloud. You can chose between MQTT and HTTP API (in fact enabling MQTT disables cloud as Shelly cannot handle both at the same time)

But mqtt does only enable the “control” but not the “informational” part correct?

Don’t know what you mean.
When you enable mqtt in shelly, everything goes through mqtt. nothing is sent to cloud.

Tasmota seems to have it… I prefer esphome (standardise).

I’m running several, all with no cloud access, but I too just want to consolidate. Just want the satisfaction of deleting another app!

Hopefully now that the Dimmer is supported on Tasmota, it will be a matter of time to make it to ESPHome. May try to go the Tasmota route for now.

Using Shelly original firmware you don’t need to use any app at all.

1 Like

Sure … but if I want to change my Wi-Fi network, I need to go back and download the app. Consolidation is key to my sanity!

I have configured almost 100 devices snd never used Shelly app (actually not even installed it).

If you have in mind migrating all shelly devices to new Wifi all at once, you can write simple script to call http api provided by each shelly device.

No app needed. All configuration can be done via browser interface.

I’ll give a good reason why I want ESPHome on my Shelly Dimmer 2. So I can invert the value of the switch inputs. That’s not possible using stock Shelly firmware.

Hi, i’ve made a configuration for the Shelly Dimmer 2 that i would like to share with the community. I worked on top of previous solutions and improved upon it. On the internet, some solutions have been shared that contain a bug in the overheating protection feature. This is fixed in the solution below. For details about the bug, please follow this link: https://github.com/esphome/issues/issues/3487#issuecomment-1537475866

The solution below contains several features:

  • several settings are explicitly mentioned to help you setup the light: gamma_correct, min_brightness, max_brightness, leading_edge, restore_mode
  • light remembers brightness value when changing from off to on
  • brightness cannot be changed when the light is off and the brightness cannot go below 0 or above 1000. Because of this, the “brigthness remembering” actually works in practice. And also because of this, the light won’t shut off when dimming down.
  • both physical buttons can be used to turn on/off the light. Long press initiates the dimming (one button for dimming up, the other button for dimming down). Long-press-release stops the dimming. The settings in the script are tuned for a full dimming in 5 seconds. You can change this to your liking. All processing is done locally on the dimmer itself.
  • Additional robustness is introduced: the dimming will stop automaticlly after 5 seconds, even if the stop-signal is missed (e.g. due to an issue with your physical switch or due to misconfiguration in Home Assistant). You change the duration to your liking.
  • 3 virtual buttons (dim up, dim down, dim stop) are introduced to Home Assistant, so you can perform super smooth dimming from the Home Assistant front-end, automations and scripts. All with minimum network traffic and all of the logic performed locally on the dimmer. I’m personally using these virtual buttons in combination with a momentary switch in the other side of the room (press-holding and press-releasing that momentary switch will initiate and stop the dimming procedure via a Home Assistant automation).
  • the logger is used in a logical way, so you can utilize it when researching and finetuning.
esphome:
  name: choose_your_light_name
  friendly_name: ${device_name}
  comment: Shelly Dimmer 2

substitutions:
  device_name: "Name of the Light"
  light_name: "Light"
  max_temp: "70" # °C

esp8266:
  board: esp01_1m

# Enable logging
logger:
    baud_rate: 0

# Enable Home Assistant API
api:
  encryption:
    key: "your encryption key"

ota:
  password: "your ota key"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "ssid name"
    password: "password"

captive_portal:

uart:
    tx_pin: 1
    rx_pin: 3
    baud_rate: 115200

light:
    - platform: shelly_dimmer
      name: ${light_name}
      id: dimmer
      leading_edge: false #choose between leading edge and trailing edge (use trailing edge for led dimming)
      min_brightness: 470
      max_brightness: 1000
      restore_mode: ALWAYS_OFF
      default_transition_length: 1s
      gamma_correct: 0 #change this to your liking. Default value is 2.8, but 0 prevents some brightness pops for me
      firmware:
        version: "51.6"
        update: true

sensor: #Important: don't change this sensor-part unless you know what you are doing. These sensors will shut the light down when overheating temperature is reached.
  # NTC Temperature
  - platform: ntc
    sensor: temp_resistance_reading
    name: Temperature
    id: temperature
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    icon: "mdi:thermometer"
    calibration:
      b_constant: 3350
      reference_resistance: 10kOhm
      reference_temperature: 298.15K
    on_value:
      then:
        - if:
            condition:
              - sensor.in_range:
                  id: temperature
                  above: ${max_temp}
              - light.is_on: dimmer
            then:
              - light.turn_off: 
                  id: dimmer
              - logger.log: "Switch turned off because temperature exceeded ${max_temp}°C"
              - homeassistant.service:
                  service: persistent_notification.create
                  data:
                    title: Message from ${device_name}
                  data_template:
                    message: Switch turned off because temperature exceeded ${max_temp}°C
    on_value_range:
      - above: ${max_temp}
        then:
          - logger.log: "Temperature exceeded ${max_temp}°C"
          - homeassistant.service:
              service: persistent_notification.create
              data:
                title: Message from ${device_name}
              data_template:
                message: Temperature exceeded ${max_temp}°C
  - platform: resistance
    id: temp_resistance_reading
    sensor: temp_analog_reading
    configuration: DOWNSTREAM
    resistor: 32kOhm
  - platform: adc
    id: temp_analog_reading
    pin: A0

switch:
  - platform: restart
    name: Reboot

script:
  - id: script_dim_down_timer
    mode: restart     # script will be kept running for 5 seconds sinces the latest time the script is executed
    then:
      - logger.log: "Dim-down timer script started"
      - delay: 5s
      - logger.log: "Dim-down timer script finished"
  - id: script_dim_up_timer
    mode: restart     # script will be kept running for 5 seconds sinces the latest time the script is executed
    then:
      - logger.log: "Dim-up timer script started"
      - delay: 5s
      - logger.log: "Dim-up timer script finished"
  - id: script_dim_down
    mode: single     # script will run once
    then:
      - logger.log: "Dim-down script started"
      - while:
          condition:
            and:
              - script.is_running: script_dim_down_timer #makes sure that dimming will stop after the set period
              - light.is_on: dimmer #prevents dimming of a light that is off
              - lambda: 'return(id(dimmer).remote_values.get_brightness() >= 0.01);' #prevents the light from going off and prevents the script from running unnecessary long (it stops at 1% brightness)
          then:
            - light.dim_relative:
                id: dimmer
                relative_brightness: -0.5%
                transition_length: 0.01s
            - delay: 0.01s
      - logger.log: "Dim-down script finished"
  - id: script_dim_up
    mode: single     # script will run once
    then:
      - logger.log: "Dim-up script started"
      - while:
          condition:
            and:
              - script.is_running: script_dim_up_timer #makes sure that dimming will stop after the set period
              - light.is_on: dimmer #prevents dimming of a light that is off
              - lambda: 'return(id(dimmer).remote_values.get_brightness() <= 0.999);' #prevents the script from running unnecessary long (it stops at 100% brightness)
          then:
            - light.dim_relative:
                id: dimmer
                relative_brightness: 0.5%
                transition_length: 0.01s
            - delay: 0.01s
      - logger.log: "Dim-up script finished"
  - id: script_turn_on_off
    mode: single
    then:
      - logger.log: "Turn_on_off script started"
      - if:
          condition:
            light.is_on:
              id: dimmer
          then:
            - light.turn_off:
                id: dimmer
            - logger.log: "Light turned off"
          else:
            - light.turn_on:
                id: dimmer
                brightness: !lambda |-
                  return id(dimmer).remote_values.get_brightness();
            - logger.log: "Light turned on with previous brightness setting"

binary_sensor:
  - platform: gpio
    name: Dim Down
    id: sensor_dim_down
    pin:
      number: GPIO12
      mode: INPUT
    internal: false
    on_multi_click:
    - timing:
        - ON for at most 300ms
      then:
        - logger.log: "Physical short press (dim_down) trigger"
        - script.execute: script_turn_on_off
    - timing:
        - ON for at least 300ms
      then:
        - logger.log: "Physical long press (dim_down) trigger"
        - script.execute: script_dim_down_timer
        - script.execute: script_dim_down
    on_release:
      then:
        - if:
            condition:
              light.is_on:
                id: dimmer
            then:
              - logger.log: "Physical dim_down release trigger"
              - script.stop: script_dim_down_timer
              - logger.log: "Script_dim_down_timer stopped"
  - platform: gpio
    name: Dim Up
    id: sensor_dim_up
    pin:
      number: GPIO14
      mode: INPUT
    internal: false
    on_multi_click:
    - timing:
        - ON for at most 300ms
      then:
        - logger.log: "Physical short press (dim_up) trigger"
        - script.execute: script_turn_on_off
    - timing:
        - ON for at least 300ms   
      then:
        - logger.log: "Physical long press (dim_up) trigger"
        - script.execute: script_dim_up_timer
        - script.execute: script_dim_up
    on_release:
      then:
        - if:
            condition:
              light.is_on:
                id: dimmer
            then:
              - logger.log: "Physical dim_up release trigger"
              - script.stop: script_dim_up_timer
              - logger.log: "Script_dim_up_timer stopped"

button:
  - platform: template
    name: Dim Down
    on_press:
      then:
        - script.execute: script_dim_down_timer
        - script.execute: script_dim_down
  - platform: template
    name: Dim Up
    on_press:
      then:
        - script.execute: script_dim_up_timer
        - script.execute: script_dim_up
  - platform: template
    name: Dim Stop
    on_press:
      then:
        - logger.log: "Stopping timer script"
        - script.stop: script_dim_down_timer
        - script.stop: script_dim_up_timer
4 Likes

Let me also share my version, based on the excellent post of @pimw above.

What I changed:

  • my Shellies are connected to wall buttons with only one button per Shelly, so I added a global boolean to switch between dim up and dim down. It changes as last action of the dimmer_stop script. It is also set based on the brightness of the light (if the light changes to 100%, it changes to false and it to true when the light is 1%). It is can also be controlled by a switch which is exposed to Home Assistant
  • to allow for other actions to be taken based on the button presses, the button presses are also sent to Home Assistant as an event. So you can create an automation in Home Assistant based on these events.
  • to allow even further control, you can disable the direct control of the light in ESPHome by use of a switch. If this switch is turned off, the ESP won’t control the light anymore, but this can be done in automations in Home Assistant. For one of my lights I’m not so interested in dimming it, but I use long press to turn off all lights of the entire floor in my house
  • in case there is no api connection with HA, it will always use the direct control of the light.
  • a double click action is added which sets the light to 100% (if light control is enabled)
  • a 5 click action is added to restart the ESP
  • I moved most of the config (like min brightness, max brightness, gamma etc) to the substitutions, so you can easily set them
  • I added a substitution for the light icon, so you can set it already in ESPHome

An example based on my config
I use packages so you can easily change your configuration in one place, and don’t need to change it for all your similar devices.
So this is the config for one of my Shelly Dimmers
I use a fixed ip for all my ESPHome devices, so that’s why it is also included here.

substitutions:
  name: "Zolder"
  lower_name: dimmer-zolder
  ip: 192.168.2.18
  max_temp: "70" # °C
  dim_lenght: "10s"
  restore_mode: "restore_default_off"
  min_brightness: "470"
  max_brightness: "1000"
  leading_edge: "false"
  gamma_correct: "0"
  light_icon: mdi:coach-lamp

packages:
  dimmer: !include packages/shelly/shelly_dimmer.yaml

This is then the configuration for the dimmer, in it I include more packages with generic config I use for all my ESPHome devices (minimal.yaml has settings like api, ota, wifi etc, basic.yaml provides sensors for uptime, wifi status, api connection status etc)
The substitutions are also added here, to act a default if they are not added above. The only one not added here is the IP address, to avoid providing the same IP twice.

# set default substitutions if not defined in config
substitutions:
  name: "Shelly Dimmer 2"
  lower_name: shelly-dimmer-2
  max_temp: "70" # °C
  dim_lenght: "10s"
  restore_mode: "restore_default_off"
  min_brightness: "470"
  max_brightness: "1000"
  leading_edge: "false"
  gamma_correct: "0"
  light_icon: mdi:lightbulb

# ESPhome generic config
esphome:
  name: ${lower_name}
  friendly_name: ${name}
  project:
    name: thefes.shelly-dimmer-2
    version: "1.0"

esp8266:
  board: esp01_1m

# load packages with default settings
# see https://github.com/TheFes/HA-configuration/tree/main/esphome/packages/0_general
packages:
  minimal: !include ../0_general/minimal.yaml
  basic: !include ../0_general/basic.yaml

# based on the config of pimw from community post https://community.home-assistant.io/t/shelly-2-dimmer/232324/18
uart:
  tx_pin: 1
  rx_pin: 3
  baud_rate: 115200

light:
  - platform: shelly_dimmer
    name: None
    id: dimmer
    icon: ${light_icon}
    leading_edge: ${leading_edge}
    min_brightness: ${min_brightness}
    max_brightness: ${max_brightness}
    restore_mode: ${restore_mode}
    default_transition_length: 1s
    gamma_correct: ${gamma_correct}
    firmware:
      version: "51.6"
      update: true
    # set the global for the dim action based on the brightness value
    on_state:
      - if:
          condition:
            - lambda: "return(id(dimmer).remote_values.get_brightness() == 1);"
          then:
            - globals.set:
                id: dim_down_bool
                value: "true"
      - if:
          condition:
            - lambda: "return(id(dimmer).remote_values.get_brightness() < 0.013);"
          then:
            - globals.set:
                id: dim_down_bool
                value: "false"
sensor:
  - platform: ntc
    sensor: temp_resistance_reading
    name: Temperature
    id: temperature
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    icon: "mdi:thermometer"
    calibration:
      b_constant: 3350
      reference_resistance: 10kOhm
      reference_temperature: 298.15K
    on_value:
      then:
        - if:
            condition:
              - sensor.in_range:
                  id: temperature
                  above: ${max_temp}
              - light.is_on: dimmer
            then:
              - light.turn_off:
                  id: dimmer
              - logger.log: "Switch turned off because temperature exceeded ${max_temp}°C"
              - homeassistant.service:
                  service: persistent_notification.create
                  data:
                    title: Message from ${name}
                  data_template:
                    message: Switch turned off because temperature exceeded ${max_temp}°C
    on_value_range:
      - above: ${max_temp}
        then:
          - logger.log: "Temperature exceeded ${max_temp}°C"
          - homeassistant.service:
              service: persistent_notification.create
              data:
                title: Message from ${name}
              data_template:
                message: Temperature exceeded ${max_temp}°C
  - platform: resistance
    id: temp_resistance_reading
    sensor: temp_analog_reading
    configuration: DOWNSTREAM
    resistor: 32kOhm
  - platform: adc
    id: temp_analog_reading
    pin: A0

# globals for dim action and light control
globals:
  - id: dim_down_bool
    type: bool
    restore_value: true
    initial_value: "true"
  - id: control_light
    type: bool
    restore_value: true
    initial_value: "true"

# switches to determine light control and dim action
switch:
  - platform: template
    name: Dim Down
    id: dim_down_active
    restore_state: false
    turn_on_action:
      - globals.set:
          id: dim_down_bool
          value: "true"
    turn_off_action:
      - globals.set:
          id: dim_down_bool
          value: "false"
    lambda: |-
      return id(dim_down_bool);
    entity_category: config
  - platform: template
    name: Control Light
    id: control_light_active
    restore_state: false
    turn_on_action:
      - globals.set:
          id: control_light
          value: "true"
    turn_off_action:
      - globals.set:
          id: control_light
          value: "false"
    lambda: |-
      return id(control_light);
    entity_category: config

script:
  - id: script_dim_down_timer
    mode: restart
    then:
      - logger.log: "Dim-down timer script started"
      - delay: ${dim_lenght}
      - logger.log: "Dim-down timer script finished"
  - id: script_dim_up_timer
    mode: restart
    then:
      - logger.log: "Dim-up timer script started"
      - delay: ${dim_lenght}
      - logger.log: "Dim-up timer script finished"
  - id: script_dim_down
    mode: single
    then:
      - logger.log: "Start dimming down"
      - while:
          condition:
            and:
              - script.is_running: script_dim_down_timer
              - light.is_on: dimmer
              - lambda: "return(id(dimmer).remote_values.get_brightness() >= 0.01);"
          then:
            - light.dim_relative:
                id: dimmer
                relative_brightness: -0.5%
                transition_length: 0.01s
            - delay: 0.01s
      - logger.log: "Dimming down stopped"
      - globals.set:
          id: dim_down_bool
          value: "false"
      - logger.log: "Toggled dim action to dim up"
  - id: script_dim_up
    mode: single
    then:
      - while:
          condition:
            and:
              - script.is_running: script_dim_up_timer
              - light.is_on: dimmer
              - lambda: "return(id(dimmer).remote_values.get_brightness() <= 0.999);"
          then:
            - light.dim_relative:
                id: dimmer
                relative_brightness: 0.5%
                transition_length: 0.01s
            - delay: 0.01s
      - logger.log: "Dimming up stopped"
      - globals.set:
          id: dim_down_bool
          value: "true"
      - logger.log: "Toggled dim action to dim down"
  - id: script_turn_on_off
    mode: single
    then:
      - logger.log: "Turn_on_off script started"
      - if:
          condition:
            light.is_on:
              id: dimmer
          then:
            - light.turn_off:
                id: dimmer
            - logger.log: "Light turned off"
          else:
            - light.turn_on:
                id: dimmer
                brightness: !lambda |-
                  return id(dimmer).remote_values.get_brightness();

binary_sensor:
  - platform: gpio
    id: sensor_button_1
    pin:
      number: GPIO14
      mode: INPUT
    internal: false
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - if:
              condition:
                - api.connected:
              then:
                - homeassistant.event:
                    event: esphome.button_pressed
                    data:
                      device: ${name}
                      click_type: restart
                      button: "1"
          - button.press: restart_button
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - homeassistant.event:
              event: esphome.button_pressed
              data:
                device: ${name}
                click_type: double
          - if:
              condition:
                or:
                  - switch.is_on: control_light_active
                  - not:
                      - api.connected:
              then:
                - light.turn_on:
                    id: dimmer
                    brightness: 100%
      - timing:
          - ON for at most 300ms
        then:
          - homeassistant.event:
              event: esphome.button_pressed
              data:
                device: ${name}
                click_type: short
          - if:
              condition:
                or:
                  - switch.is_on: control_light_active
                  - not:
                      - api.connected:
              then:
                - script.execute: script_turn_on_off
      - timing:
          - ON for at least 300ms
        then:
          - homeassistant.event:
              event: esphome.button_pressed
              data:
                device: ${name}
                click_type: long
          - if:
              condition:
                or:
                  - switch.is_on: control_light_active
                  - not:
                      - api.connected:
              then:
                - if:
                    condition:
                      lambda: |-
                        return id(dim_down_bool);
                    then:
                      - script.execute: script_dim_down_timer
                      - script.execute: script_dim_down
                    else:
                      - script.execute: script_dim_up_timer
                      - script.execute: script_dim_up
    on_release:
      then:
        - homeassistant.event:
            event: esphome.button_pressed
            data:
              device: ${name}
              click_type: release
        - if:
            condition:
              or:
                - switch.is_on: control_light_active
                - not:
                    - api.connected:
            then:
              - if:
                  condition:
                    light.is_on:
                      id: dimmer
                  then:
                    - script.stop: script_dim_down_timer

button:
  - platform: template
    name: Dim Down
    on_press:
      then:
        - script.execute: script_dim_down_timer
        - script.execute: script_dim_down
  - platform: template
    name: Dim Up
    on_press:
      then:
        - script.execute: script_dim_up_timer
        - script.execute: script_dim_up
  - platform: template
    name: Dim Stop
    on_press:
      then:
        - logger.log: "Stopping timer script"
        - script.stop: script_dim_down_timer
        - script.stop: script_dim_up_timer

In total it looks like this in ESPHome (2 sensors are added by the Fritz!Box integration, the one to enable the internet connection, and the device_tracker)

3 Likes