Smooth dimming of zigbee groups with Hue Tap Dial via zigbee2mqtt

Hi everyone,

I have zigbee2mqtt and Hue Tap Dial dimmer switches, and I’d like to use the dial to dim lights. In most cases I want to control a zigbee group (all lights in the room). Using the excellent blueprint from @freakshock Philips Hue Tap Dial Switch - Zigbee2MQTT I am able to adjust brightness based on step,slow and fast turns of the dial - but this gets triggered multiple times a second. For zigbee groups, they can only receive about 1 command a second.

I have been trying to make a “rate limiting” script/automation, but have hit various issues. Basic idea:

  • A helper to store the pending brightness change (number from -255 to 255)
  • An automation triggered on any turn of the dial, that adds/subtracts from the helper
  • A second automation triggered on changes to the helper to a non 0 value, that changes uses brightness_step to adjust the brightness, resets the helper to 0, then waits 1 second, repeating until it stays 0. This is in “single” mode thus limiting how often brightness commands are sent.

Initially i was just using “step” as 25, “slow” as 50, “fast” as 100 in terms of the amount subtracted/ added, but this isn’t really precise enough. I then saw that there is a step_size (always a positive integer) and a step_direction (left/right) set - so I tried adjusting my first automation to use this, but I’ve found that “size” and “direction” aren’t being set simultaneously - and immediately get set back to “None”, so it gets very complicated trying to capture both of these values.

Below is the last basically working version of the automation - it works for increasing brightness, but almost never catches the “left” direction so it’s always increasing, never decreasing, brightness.

I feel like this must be a common issue that someone else has solved (rate limiting for zigbee groups, smooth dimming with the tapdial, or capturing the two different values that change at different times) - so would appreciate any ideas or pointers!

automation 1, sets helper:

alias: Tapdial helper test 3
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.officejames_tapdial_action_step_size
    from: None
    id: size
condition: []
action:
  - action: input_number.set_value
    metadata: {}
    data:
      value: >-
        {{ states('input_number.tapdial_test_helper_3')|int + (-1 if
        states('sensor.officejames_tapdial_action_direction') == 'left' else
        1)*states('sensor.officejames_tapdial_action_step_size')|int(0) }}
    target:
      entity_id: input_number.tapdial_test_helper_3
    enabled: true
mode: single

automation 2, sets brightness

alias: Tapdial dim test 2
description: ""
trigger:
  - platform: state
    entity_id:
      - input_number.tapdial_test_helper_3
condition:
  - condition: not
    conditions:
      - condition: state
        entity_id: input_number.tapdial_test_helper_3
        state: "0.0"
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 0
      milliseconds: 100
    enabled: true
  - repeat:
      sequence:
        - target:
            entity_id: light.officejames_lamp
          data:
            brightness_step: "{{ states('input_number.tapdial_test_helper_3')|int}}"
          action: light.turn_on
        - action: input_number.set_value
          target:
            entity_id: input_number.tapdial_test_helper_3
          data:
            value: 0
        - delay:
            hours: 0
            minutes: 0
            seconds: 0
            milliseconds: 900
          enabled: true
      until:
        - condition: state
          entity_id: input_number.tapdial_test_helper_3
          state: "0.0"
mode: single

Visual, showing the helper value changing and being reset as expected (except it’s always increasing even though I was turning left)
image

and here you can see that the values for step size goes from 8 to None, and then the direction goes from left to None (but sometimes it’s the other way round), which I believe is why I’m not getting negative numbers
image

I was looking into this for my Tap dial automations a few months ago. Didn’t figure it out, but I did find something interesting - the going back to “None” behaviour is controlled by the legacy_triggers: true in Z2M.

In my case, I wasn’t willing to disable that since it seems to be a global setting (not device specific). I have other devices where this “None” value is desirable, so I stopped trying to fight it.

Hope the info helps you to fix your problem.

Ooo, thanks for the tip! I will try it out - i can’t think of anything right now that relies on it being none… I suppose it then becomes a “last action” value and in some cases you’d need to check when it changed too.

Depending on how many there are, could you disable the setting and create helper entities that behave in the other way (change to the value then immediately go back to None) with an automation?

First off, I was wrong - it’s legacy_triggers which controls this setting. The comments being above the affected setting in Z2M always throw me off. I have edited my original post to correct this.

To answer your question, it’s probably possible to set this individually per entity with a template sensor or helper and an automation, however this could get cumbersome pretty fast.

In your current case, so far it would be pretty straightforward given that you’d need it for a single entity (action) on a single device. The problem with maintaining this behaviour is that you might add more devices for which you want to apply this behaviour in future.

Yeah, agreed on it getting cumbersome. I’ve actually just done something similar for a different use case - I wnated my (Unifi Protect) doorbell to trigger a notification on Alexa - but using Matterbridge (local) instead of Alexa Media Player as I can not get AMP to stay working for more than a couple of weeks without intervention.

The doorbell press action wasn’t getting picked up by Alexa because it was only for <1 second so I wanted a “lagging indicator”.

Initially used a single template sensor “if state updated in the last 5 seconds” … I couldn’t figure out how to limit that to “if it changed to on in the last 5 seconds” … this was simple and it worked, except of course it triggered for any state change. Cue me being woken up and freaked out at 3am by Alexa telling me “someone is at the door” when the doorbell reset for some reason!

I then changed it so I have a button helper that I set to on for 5 seconds then of for 5 seconds via an automation triggered by doorbell press (it does other things too), and my template sensor just mirrors the state of that (Alexa routines seem to be triggerable on binary_sensors but not buttons). So yeah, messy/cumbersome if I was to try and do this for multiple things!

I’m hardly an expert in templating, but the below is what I use (copied from someone in the forums) to exclude the unwanted states.
Note that this is a condition inside an automation, so you might have to tweak it a little to suit your template:

{{ trigger.from_state.state not in ['unknown', 'unavailable'] }}

Ah yes, I had forgotten you can have trigger-based template sensors. Or I think you can. haven’t tried it yet :slight_smile: