Hue Lights brightness during transition

Hello all,

This is driving me crazy.
I have a bunch of Hue lights paired to the bridge pro (I would like to keep it that way if at all possible because the hue app is much easier to use for my family members and it “just works”).

I wanted to setup a fairly straightforward (or so I thought…) “hold button down” → make lights in a hue room progressively brighter until user releases the button → stop at that level of brightness that the user sees.

I have had to go down a rabbit hole of how to figure out what the current brightness of the bulb is as the dimming is happening.

Started as sequential brightening on each press of the button but wanted to have a smoother transition and the lag after each press was making it difficult to land on the right setting.

Then I tried to estimate it, based on the speed of transition and the time when the user pressed the button, but there is always a gap between what the user sees and the estimated brightness.

And finally I tried to just force a refresh with an update_entity triggered from HA when the user releases the button but this just sent me to max brightness (because I assume the bride does not share brightness mid-transition).

Has someone solved that issue ? Any pointers welcome !

The following is a fragment from one of my automations for controlling a light’s brightness via a remote-control.

If the button event is either brightness_move_down or brightness_move_up, it calculates the following variables:

brightness_from: The light's current brightness level.
brightness_target: The target brightness; 5 if dimming, 255 if brightening.
brightness_delta: The difference between the light's current brightness and the target brightness.
transition_time: The time it will take to dim/brighten from the current brightness to the target brightness. Uses a predetermined fade speed.
fade_speed: If dimming then make the fade speed a negative value.

Then it simply uses light.turn_on and sets transition and brightness. So if the light is currently at 20% and the user presses and holds the ‘brighten’ button, the light begins transitioning from 20% (51) to 100% (255). The light itself performs the brightening process (due to the use of the transition option).

    - variables:
        fade_speed: 60
        event: "{{ trigger.to_state.attributes.event_type }}"
    - choose:
        - conditions: "{{ event in ['brightness_move_down', 'brightness_move_up'] }}"
          sequence:
            - variables:
                brightness_from: "{{ state_attr(light, 'brightness') | int(0)}}"
                brightness_target: "{{ iif(event == 'brightness_move_down', 5, 255) }}"
                brightness_delta: "{{ (brightness_from - brightness_target) | abs }}"
                transition_time: "{{ brightness_delta / fade_speed }}"
                fade_speed: "{{ fade_speed * iif(event == 'brightness_move_down', -1, 1) }}"

            - action: light.turn_on
              target:
                entity_id: '{{ light }}'
              data:
                transition: '{{ transition_time }}'
                brightness: '{{ brightness_target }}'

The final part is detecting when the user releases the ‘brighten’ button via a brightness_stop event. The automation uses a wait_for_trigger to detect the button event.

Upon detecting the event, it uses light.turn_on to set the light’s brightness level.

It computes the brightness based on how long the user pressed and held the button (the starting brightness plus the amount of time that has elapsed multiplied by the fade speed). So if the user releases the button when the light is approximately 80% brightness, the automation executes a light.turn_on command with brightness set to 204 (80%). This stops the light’s transition process (which was increasing to 100% brightness) and sets it to the user’s desired final brightness level.

        - conditions: "{{ event in ['brightness_move_down', 'brightness_move_up'] }}"
          sequence:
            - variables:
                brightness_from: "{{ state_attr(light, 'brightness') | int(0)}}"
                brightness_target: "{{ iif(event == 'brightness_move_down', 5, 255) }}"
                brightness_delta: "{{ (brightness_from - brightness_target) | abs }}"
                transition_time: "{{ brightness_delta / fade_speed }}"
                fade_speed: "{{ fade_speed * iif(event == 'brightness_move_down', -1, 1) }}"

            - action: light.turn_on
              target:
                entity_id: '{{ light }}'
              data:
                transition: '{{ transition_time }}'
                brightness: '{{ brightness_target }}'

            - wait_for_trigger:
                - trigger: state
                  entity_id: event.office_dimmer_action
                  not_from: 'unavailable'
                  not_to: 'unavailable'
              timeout: '{{ transition_time }}'
              continue_on_timeout: false

            - if: "{{ wait.trigger.to_state.attributes.event_type == 'brightness_stop' }}"
              then:
                - action: light.turn_on
                  target:
                    entity_id: '{{ light }}'
                  data:
                    brightness: >
                      {{ (brightness_from + fade_speed * (wait.trigger.to_state.last_changed - trigger.to_state.last_changed).total_seconds()) | round(0) }}

I have used this technique successfully, for the past two years, for several lights. Responsiveness is satisfactory, largely because there are only two Zigbee commands involved, the first starts the automatic dimming/brightening process and the second one stops it and sets the desired brightness.

1 Like

Hey - thks for taking the time to make a detailed reply.

Yes I tried this method

alias: Office TP Lights Toggle second try
description: ""
use_blueprint:
  path: Jonybat/aqara_ws-k07e_event_entity.yaml
  input:
    event: event.h1_office_tp_action
    helper_last_controller_event: input_text.h1_office_tp_last_pressed
    helper_last_event_time: input_text.h1_office_tp_last_pressed
    hold_down:
      - action: input_number.set_value
        target:
          entity_id: input_number.office_tp_hold_start
        data:
          value: "{{ now().timestamp() | int }}"
      - action: input_number.set_value
        target:
          entity_id: input_number.office_tp_brightness_at_hold
        data:
          value: "{{ state_attr('light.office_tristan', 'brightness') | int }}"
      - condition: template
        value_template: "{{ prev_command != 'hold_down' }}"
      - action: light.turn_on
        target:
          entity_id: light.office_tristan
        data:
          brightness: 255
          transition: 8
    release_down:
      - variables:
          elapsed: >
            {{ now().timestamp() | int -
            states('input_number.office_tp_hold_start') | int }}
          brightness_at_hold: "{{ states('input_number.office_tp_brightness_at_hold') | int }}"
          estimated_brightness: >
            {% set per_second = (255 -
            states('input_number.office_tp_brightness_at_hold') | int) / 8 %} {%
            set estimated = states('input_number.office_tp_brightness_at_hold')
            | int + (per_second * elapsed) %} {{ [estimated | int, 255] | min }}
      - action: light.turn_on
        target:
          entity_id: light.office_tristan
        data:
          brightness: "{{ estimated_brightness }}"
          transition: 0

But my issue was that the lag between the zigbee commands and my hue bridge pro reacting messes up with the calculation so I ended up basically always being at 100%.

If I’m not mistaken you are driving your lights directly in HA through your zigbee bridge ?

I am controlling Hue bulbs that communicate with Home Assistant via a Hue Bridge and the Philips Hue integration.

I also use the same technique to control the fading of various IKEA lights (via Zigbee2MQTT and a Sonoff Zigbee Dongle Plus). Same responsive and reliable control.

The code you posted are the parameters supplied to a blueprint. You may wish to compare the blueprint’s code to mine. For example, in my version, releasing the button is an event handled within the same code block that detects holding the button (it seems logical to me because after holding there’s a release). There may be other differences.

So you think the release_down trigger is delayed because it’s in a blueprint rather than in the same automation ?

Am also a bit unclear on how you make the button fire named events like you have ? Could you please point me in the right direction there ?

In my case I will only use hold_down for brightening up as I have no way to know what the user wants to do (later I will have to figure out a way to do up and down continuously while the user stays pressed on the button…)

Here’s the automation I use to control a Philips Hue light with an IKEA 1743 remote-control.

  • Replace the one instance of light.office with your light entity.
  • Replace the four instances of event.office_dimmer_action with your event entity.
  • Change the names of the button events to match whatever your remote-control uses.
Click to reveal automation
## 2024-02-15 IKEA E1743 remote control
## Button actions:
## on, off, brightness_move_down, brightness_move_up, brightness_stop
##
## Single-click for fading on/off
## Double-click for immediate on/off
## 
alias: Remote Office Dimmer
id: remote_office_dimmer
mode: single  
max_exceeded: silent

variables:
  interval: 1
  input_light: light.office
  fade_speed: 60
  fade_off: 3
  default_brightness: 128

triggers:
  - trigger: state
    entity_id: event.office_dimmer_action
    not_from: 'unavailable'
    not_to: 'unavailable'

conditions: []

actions:
  - variables:
      event: "{{ trigger.to_state.attributes.event_type }}"

  - choose:
      - conditions: "{{ event == 'on' }}"
        sequence:
          - wait_for_trigger:
              - trigger: state
                entity_id: event.office_dimmer_action
                not_from: 'unavailable'
                not_to: 'unavailable'
            timeout: '{{ interval }}'
            
          - action: light.turn_on
            target:
              entity_id: '{{ input_light }}'
            data:
              brightness: "{{ 255 if wait.completed and wait.trigger.to_state.attributes.event_type == 'on' else default_brightness }}"


      - conditions: "{{ event == 'off' }}"
        sequence:
          - wait_for_trigger:
              - trigger: state
                entity_id: event.office_dimmer_action
                not_from: 'unavailable'
                not_to: 'unavailable'
            timeout: '{{ interval }}'
          - action: light.turn_off
            target:
              entity_id: '{{ input_light }}'
            data:
              transition: "{{ 0 if wait.completed and wait.trigger.to_state.attributes.event_type == 'off' else fade_off }}"


      - conditions: "{{ event in ['brightness_move_down', 'brightness_move_up'] }}"
        sequence:
          - variables:
              brightness_from: "{{ state_attr(input_light, 'brightness') | int(0)}}"
              brightness_target: "{{ iif(event == 'brightness_move_down', 5, 255) }}"
              brightness_delta: "{{ (brightness_from - brightness_target) | abs }}"
              transition_time: "{{ brightness_delta / fade_speed }}"
              fade_speed: "{{ fade_speed * iif(event == 'brightness_move_down', -1, 1) }}"

          - action: light.turn_on
            target:
              entity_id: '{{ input_light }}'
            data:
              transition: '{{ transition_time }}'
              brightness: '{{ brightness_target }}'

          - wait_for_trigger:
              - trigger: state
                entity_id: event.office_dimmer_action
                not_from: 'unavailable'
                not_to: 'unavailable'
            timeout: '{{ transition_time }}'
            continue_on_timeout: false

          - if: "{{ wait.trigger.to_state.attributes.event_type == 'brightness_stop' }}"
            then:
              - action: light.turn_on
                target:
                  entity_id: '{{ input_light }}'
                data:
                  brightness: >
                    {{ (brightness_from + fade_speed * (wait.trigger.to_state.last_changed - trigger.to_state.last_changed).total_seconds()) | round(0) }}

    default: []
1 Like

Just to report back I have used your light brightness automation extensively and it works as expected. Thank you very much!

Modified it slightly to loop through cycles from high to low instead of getting stuck at the first end.