Integrate Philips Hue Tap Dial Switch into Home Assistant to create a powerful remote control, supporting single tap, double tap, hold, and (4 different) rotate functionality.
Since this project has garnered significant attention and is widely used, we’ve transformed the automation into a straightforward and user-friendly blueprint. Enjoy!
Hello @fleeman Congratulations on the great blog! I love the way you control the Tap Dial. I’d like to do something similar, but I don’t think it’s possible without being in developer mode. I’m using Z2M, and I haven’t seen any mention of double-click. I’d like to be able to control both single-click and double-click, and have the wheel handle the device associated with the button, just like you do. But I don’t think it’s possible with Z2M. If you have more information, I’d appreciate it. I’ll be receiving Button
this weekend.
That is not an issue, the automation and helpers take care of double click detection and different dial options. If the button presses work, my solution will work too.
What should I put in place of the Deconz event types? I’m looking to transform them, but as I can see that you have a higher level of knowledge than I do, if you have already set up automation in Z2M, I would appreciate it. I have already created the two helpers. After that, you mentioned that the button allows you to switch between Hue scenes or the amplifier source. How do you do that? I don’t quite understand that part either. Thank you again for sharing; it’s great. If the double click function works, it really adds a lot to this button
I started modifying your code. Single click and hold functions work well, but I’m having trouble modifying the triggers for rotate and double-click. Essentially, I have this MQTT path and actions that I want to use: “zigbee2mqtt/hue_tap_dial_switch/action”:
dial_rotate_right_fast
dial_rotate_right_step
dial_rotate_right_slow
dial_rotate_left_fast
dial_rotate_left_step
dial_rotate_left_slow
button_1_press_release
button_2_press_release
button_3_press_release
button_4_press_release
button_1_hold
button_2_hold
button_3_hold
button_4_hold.
If someone could help me finish, I would appreciate it because I’m a bit stuck right now.
Well this is just awesome - thank you for the blog post! I’m a newcomer to HA but a veteran developer, and this is just the right depth for me to start digging into more complex automations.
The post says it should work with any Zigbee integration that can detect the device, but between this thread’s Z2M users and my own experience with ZHA, that should include the big caveat that event data structure is quite different between the three Zigbee integration options. The LOGIC is identical (probably) but the triggers and conditions all seem to require modification.
I’m trying to figure this out myself with the events debug log… but the examples of other integrations I see on github all seem to use more semantic identifiers and are generally more straightforward that what I find myself coming up against here. Am I approaching this wrong? Help and tips are welcome.
Based on the events debug log, here are the differences I’ve noted from the code sample in the blog post:
s/deconz_/zha_.
Both rotation and button events in ZHA are emitted in zha_events. I can’t find mention of any other event types in ZHA, and zha_relative_rotary_event is empty.
Identifier for the device is different. All events from the switch come in with device_id=f1d484eecfd7ae66406d537f38c66ebf. Is that unique to this individual button? I can’t find anything else in the debug log that is a consistent identifier across all interactions from the device.
Identifier for button presses vs rotation is different. looks like command: recall vs command: step_with_on_off is the way to do it.
The 4 buttons are identified by the scene_id parameter: 1, 0, 5, or 4, respectively for buttons 1, 2, 3, 4.
Long press triggers an event that looks like a rotation, with command: step_with_on_off and the same params structure as rotations.
For reference, here’s what the different kinds of events look like in my debug log:
So I separated the automations into two for now. One handles the buttons, and I only consider the “press release” event for the first click, as otherwise, I get both “press” and “press release” events. The other handles the “hold” action. The other automation only takes the “dial” actions to separate the three settings: brightness step slow and fast. It’s working well, although it’s not optimized, but I’m taking it step by step. I’d like to add the double-click, so I think I would need to create an automation that doesn’t trigger the other one with a single click, but it’s not easy. If you have any ideas, please let me know.
I can’t figure out the double press, and the single one doesn’t work with this code. I can’t find where my error is. If someone could give me advice on where the error might be, I have error logs for data values every time it runs, and the code stops even though my counter is incrementing, and the input text corresponds to the value changes, but it won’t execute the actions. Thank you.
This morning, I went shopping and bought a Home Assistant SkyConnect and installed Zigbee2MQTT aside Phoscon specifically to rewrite this automation for all you Z2M users. I’ve also updated the blog post on smarthomegeeks.io. Enjoy:
Thank you very much. I had an issue with my direct MQTT trigger, but it works better when passing through the Home Assistant sensor. In any case, thank you very much. However, I think there might be an issue with the “hold” functionality because it seems to filter it out and only lets the “release” events pass through, if I’m not mistaken. The double click works great, though. Thanks again, you’re amazing!
I think you need to make this small correction to the trigger. With this, everything works perfectly for me
value_template: >-
{{ trigger.event.data.new_state.state is match('^(dial_rotate_|button_[1-4]_(press|hold))') and
trigger.event.data.new_state.state != '' }}
For the light adjustment, what parameters did you set to make it smooth? I have Hue lights controlled by the Hue Bridge, and it works fine. I kept my three speed settings in my dial code, but I might switch to just one speed if I find a value that suits me
@ohthehugemanateeohthehugemanatee Your post is almost a month old but I wanted to reply because I’m currently implementing ZHA automations for the Tap Dial Switch. There are ZHA blueprints available for this device and those work fine. My issue right now is that ZHA support for this device is limited. For example long press buttons are not available right now (see below). I also wanted to provide some feedback on your questions.
That is correct.
No.
Correct.
That is also correct. There is currently only 1 long press trigger which is the same for all buttons. This seems to be a ZHA problem. I have create a bug report for it here: https://github.com/home-assistant/core/issues/104491
@starob thank you for replying! I had a busy month and couldn’t get back to it until now, so your post was timely even a month after my OP.
I gather we have better working blueprints for Z2M. But all my other devices are already working through ZHA and AFAIK I can only use one or the other… and I don’t think I have it in me to completely redo all my devices just for the sake of the tap dial switch haha. Please correct me if I’m wrong!
Anyway it turned out to be pretty simple to take care of the different signals from the device. I added a step to map the values to a human-readable “button_pressed” local variable:
Great: now I can refer to the pushed_button variable for an integer which is directly comparable to the number variable I have stored.
So now I have the capabilities I need for single and double press, at least. But the logic is confusing to me. How SHOULD this work? Here’s my faux code understanding… but this does not map to what I see in the example.
if (input_number.button_last_pressed == pushed_button && counter.button_counter > 0) {
// This is a double press.
counter.button_counter++
// Switch on pushed_button value for action.
// stop execution
}
if (counter.button_counter == 0) {
// This is a single press.
sleep 1
if (counter.button_counter > 1) {
// A second press happened in a parallel thread. Let the action happen there.
// stop execution
}
if (counter.button_counter == 1) {
// no double press happened in a parallel thread.
// Switch on pushed_button value for action.
// stop execution
}
You can run ZHA and Zigbee2MQTT in parallel if you have a second coordinator (e.g. Zigbee USB stick). Since I had a spare Zigbee stick available I did this to check the support of Z2M for this device and it is far better than ZHA. It supports every possible short & long press etc. Also the implementation to update the firmware of zigbee devices is much better in my opinion.
Since I have ZHA and Z2M running in parallel I might migrate to Z2M over time or just keep it running until ZHA improves the support for this device and then remove Z2M again.
I highly recommend transitioning to Z2M as it offers compatibility with a broader range of devices and more attributes per device. In my own setup, I’m using two coordinators to gradually migrate from deconz to Z2M. This approach allows me to switch one device at a time, ensuring my smart home remains functional during the transition. Investing in an additional Zigbee stick is worth the expense for a smoother changeover.
That’s interesting advice… I have read elsewhere that ZHA is usually recommended because it has the weight of homeassistant’s community behind it and is easier to work with in the GUI. I’ve really appreciated how all my lights “just worked” so far. My only complaint is sometimes messages aren’t received by all devices… which I assume is a limitation of the zigbee protocol .
I sat down to think this through more in fake code, and came up with a simpler and more flexible version.
This version
only needs one global variable, a string that records the whole sequence
is (I think) easier for users to add custom behaviors, because there’s just one place for all your actions. New actions are as easy as e.g. if button_sequence == "222"
lets you respond to any combination of buttons and rotations. Want to respond to “1224, then fast left turn, then 321, then slow right turn”? You can do that.
// mock code only
GLOBAL button_sequence string = ""
function button_is_pushed(button_pushed string) {
// No matter what, add to the sequence string
// Separate rotation codes with a space.
if (button_pushed == "R" || button_pushed == "L") {
rotation = true;
}
if (rotation) {
button_sequence = button_sequence + " ";
}
button_sequence = button_sequence + button_pushed;
// Separate rotation codes with a space.
if (rotation) {
button_sequence = button_sequence + " ";
}
// Track the state of the button sequence locally to this run.
local_button_sequence = button_sequence;
// Wait 1 second and see if any further pushes have modified the sequence.
sleep 1
if (local_button_sequence != button_sequence) {
// Another parallel run added to the button sequence. Let execution happen there.
exit 0;
}
// Parse the string into an array for convenience.
sequence_array = string_split(local_button_sequence, " ");
/**
* All your actions go here.
* You can use either the string version of the sequence (e.g. "1224 R34"),
* or the array is easier when rotations are involved. (e.g. [ "1224", "R34"] )
*/
// Example single tap.
if (local_button_sequence == "1") {
scene_trigger("living_room_overhead_light_on");
}
// Example double tap.
if (local_button_sequence == "11") {
scene_trigger("living_room_all_lights_on");
}
// Example combination taps and rotation.
if (sequence_array[0] == "1224" && substr(sequence_array[1], 0, 1) == "R" && sequence_array[2] = "4221") {
text_to_speech("Access granted");
automation_trigger("secret_lair_mode_activate");
}
// reset the button sequence.
button_sequence = ""
}