Skip to content

Support smooth dimming of lights #3207

@nohat

Description

@nohat

Describe the problem you have/What new integration you would like

ESPHome's current light component lacks the capability for smooth, continuous dimming control, similar to the "hold-and-release" functionality found in many commercially available smart lighting devices. This prevents users from achieving an intuitive and responsive smart lighting experience.

For reference, modern comprehensive smart home control APIs provide three different user interaction modalities for setting the dimming level of a light. Leveraging the terminology used in the Zigbee and Matter specifications, they are:

  1. Move to Level command: Specify a target dimming level and a transition time.
  2. Step command: Specify an adjustment up or down in the level from the current level by a given step size over a given transition time.
  3. Move/Stop commands: The "Move" command tells the light to begin adjusting the dimming level up or down at a particular rate. The light then increases or decreases dimming continuously until it reaches the minimum or maximum dimming level, OR it receives a "Stop" command, halting the dimming at the current level.

ESPHome already supports modalities 1 and 2 through the light.turn_on and light.dim_relative actions but it does not have explicit support for the "Move" and "Stop" commands. While it is possible to get the effect of "Move" by sending a light.turn_on action to minimum or maximum dimming level with an appropriate transition length, ESPHome does not at present have any way to properly halt in-progress dimming. A transition in progress can be halted by sending a new light.turn_on command but it will use the brightness level specified in the command or the target brightness from the original transition—not the midway point in the transition when the command is sent.

Please describe your use case for this integration and alternatives you've tried:

My primary use case is to provide an intuitive and responsive smart lighting control experience when running ESPHome on e.g. a Martin Jerry SD01 dimmer, which has a PWM light output and 3 buttons—main, up and down. When I hold the up or down button, I expect the light brightness to change smoothly and continuously, stopping precisely when I release it. This mirrors the behavior of physical dimmer switches and dedicated smart dimmers.

Currently, achieving this in ESPHome requires complex workarounds. I've attempted to use rapid-fire light.turn_on commands with short transitions, but this leads to a choppy, non-smooth dimming effect and significantly increases system overhead and network traffic, especially when integrated with Home Assistant. The lack of ability to start a continuous adjustment without a predetermined target, or to halt an ongoing transition while preserving its intermediate state, forces these suboptimal solutions. I have implemented a workaround in my ESPHome packages repo, but it uses a clunky combination of global variables, an on_loop lambda, and spamming the Light component with many turn_on calls while a dimming button is held down.

This feature would enable:

  • Intuitive Control: Users could hold a button to dim up or down, releasing it at their desired brightness, matching common user expectations from commercial devices.
  • Precision: Users could stop at exact desired brightness levels without being limited to predefined steps.
  • Responsiveness: Local execution within ESPHome would eliminate network latency, providing immediate and smooth adjustments.
  • Accessibility: Continuous adjustment is often easier for users with fine motor skill limitations compared to stepped adjustments.

Additional context

This feature is essential for aligning ESPHome with modern smart home user experience expectations and bridging the gap with capabilities already present in many smart lighting protocols and device firmwares. While Home Assistant currently lacks native, first-class support for smooth dimming, providing this capability at the ESPHome level would empower users to build superior lighting controls.

I added a similar feature to Tasmota in 2021, and it's time to add it to ESPHome. I have a longer-term goal of building support into Home Assistant to allow cross-platform smooth dimming—e.g. use the up/down buttons of Lutron Pico Remotes to enable smooth dimming of a group of Zigbee bulbs and ESPHome-powered smart dimmers. However, I want to first ensure the open source firmware ecosystem has solid on-device support.

The implementation should build upon the existing transition infrastructure within ESPHome's light component. The key missing functionalities to enable ability to provide intuitive "hold-and-release" dimming functionality are:

  • The ability to initiate continuous brightness/color adjustments without predefined start and end points.
  • The ability to halt ongoing transitions instantly while accurately preserving the current intermediate light state (brightness/color).

Concerns regarding complexity, resource usage, design philosophy, and maintenance burden can be addressed:

  • Complexity: The feature can leverage existing transition infrastructure with minimal new code paths, and a modular design could allow optional compilation and gradual rollout.
  • Resource Usage: Efficient implementation should reuse existing mechanisms, leading to negligible overhead (prototypes show minimal memory increase and CPU impact).
  • Design Philosophy: This feature aligns with ESPHome's local-first approach and empowers users with advanced control from simple YAML config.
  • Maintenance Burden: A clear API design and comprehensive unit tests reduce long-term support costs.

Example YAML config

esphome:
  name: smooth_dimmer_device
  project:
    name: Martin Jerry.SD01 Smart Dimmer
    version: "1"

esp8266:
  board: esp01_1m
  restore_from_flash: true

# Define the PWM output for the light
output:
  - platform: esp32_pwm
    pin: GPIO2
    id: light_pwm_output

# Define the light component
light:
  - platform: monochromatic
    output: light_pwm_output
    name: "Living Room Light"
    id: living_room_light

# Define the binary sensors for dimming up and down
binary_sensor:
  - platform: gpio
    name: "Dim Up Button"
    pin:
      number: GPIO0
      inverted: true
      mode: INPUT_PULLUP
    on_press:
      # Start dimming up when the button is pressed and held, with a specified speed
      - light.start_dimming:
          id: living_room_light
          direction: up
          speed: 0.25 # Example: Change brightness by 25% per second
    on_release:
      # Stop dimming when the button is released
      - light.stop_dimming: living_room_light

  - platform: gpio
    pin:
      number: GPIO1
      inverted: true
      mode: INPUT_PULLUP
    name: "Dim Down Button"
    on_press:
      # Start dimming down when the button is pressed and held, with a specified speed
      - light.start_dimming:
          id: living_room_light
          direction: down
          speed: 0.25
    on_release:
      # Stop dimming when the button is released
      - light.stop_dimming: living_room_light

# Optional: Add a button for toggling the light on/off
  - platform: gpio
    pin:
      number: GPIO15
      mode: INPUT_PULLUP
    name: "Light Toggle Button"
    on_press:
      - light.toggle: living_room_light

Note: Alternative API implementations, such as adding parameters to the light.turn_on service or using different service or parameter names, should be considered. This is just a notional example.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions