Leviton ZW6HD + Home Assistant: "Off" command overwrites last-known dim level (turns on to 1%)

I have 67 ZW6HD dimmers running in Home Assistant via Z-Wave, and they work well overall. However, there is a consistent reproducible bug (or configuration issue) I cannot resolve.

Steps to reproduce:

  1. Set any ZW6HD dimmer to any level (20%, 50%, 80%, 100%) via Home Assistant.
  2. Turn the switch off from within Home Assistant (not manually at the wall).
  3. Walk to the wall switch and turn it back on manually.

Result: The switch comes on at 1% regardless of what the prior dim level was.

Expected result: The switch should restore to the last active dim level, exactly as it does when you turn it off and on manually at the wall.

What works correctly:

If I set the dimmer to any level (e.g., 55%) and turn it off manually at the wall, then turn it back on, it correctly restores to 55%. This behavior works every time.

Root cause hypothesis:

I believe the Z-Wave implementation in Home Assistant is sending a “dim to 0%” command rather than a true Binary Switch Off. This overwrites the switch’s last-known level memory with ~0%, so when the switch is powered back on it restores to that near-zero value instead of the previous setting.

A proper off command should toggle the relay state without touching the dim level register.

Environment:

  • Switch: Leviton ZW6HD (x67)
  • Integration: Home Assistant Z-Wave (JS or ZWave2MQTT, whichever applies)
  • Behavior: 100% reproducible

Has anyone seen this and found a fix, whether a parameter setting on the switch, a Z-Wave command class configuration in HA, or something else?

The light entity in HA corresponds to Command Class Multilevel Switch, not Binary Switch. In order to turn off a Z-Wave dimmer switch you do send a Multilevel Switch Set command with the value 0% (which is also what HA is doing).

None of my Jasco nor Zooz dimmers behave the way you are describing.

To confirm the behavior yourself, turn on debug logging in the integration page, turn the light off in HA and watch the Websocket messages HA sends to the driver, and then view the subsequent debug logs from the driver sending the command to the device. Then you could additionally turn on the light and see what it reports.

Thanks for the detailed response and the spec reference. I followed your suggestion exactly and captured Z-Wave JS debug and Silly level logs. Here is what I found.

HA behavior confirmed

You are correct. I read the HA source code at the link you provided. async_turn_off calls _async_set_brightness(0) which sends MultilevelSwitchCCSet(0). I verified this in my logs. The command is spec-compliant per Table 2.403.

What the logs show

I ran a controlled sequence covering all combinations of HA vs wall for both on and off. Summary:

Off method On method Prior level Restored to
Wall Wall 48 48 (correct)
HA (0x00) HA (0xFF) 48 48 (correct)
HA (0x00) Wall paddle 50 1 (wrong)

The only failing combination is HA off followed by physical paddle on.

What the Silly log adds

When HA sends the off command, Z-Wave JS logs: calling SET_VALUE API MultilevelSwitchCCAPI: property: targetValue, optimistic: true. This is the only behavioral difference between an HA off and a wall off at the Z-Wave level. The wall off produces no outbound command at all. The switch simply reports its own state changing via unsolicited MultilevelSwitchCCReport frames with targetValue: 0.

So the switch receives two different signals for “off”: an explicit MultilevelSwitchCCSet(0) from HA, and nothing from a wall press (it just self-reports). The restore behavior differs depending on which path was used.

Per the spec

Table 2.403 designates 0x00 as a state control command indicating Off, not a level assignment. The switch should preserve its last non-zero level regardless of how it received the off signal. When turned back on via the physical paddle after receiving MultilevelSwitchCCSet(0), my ZW6HD reports currentValue: 1, targetValue: 1 every time. When turned back on via HA (0xFF) after the same command, it correctly restores to the prior level.

Device details

  • Switch: Leviton ZW6HD (x67 units)
  • Firmware: 1.1.0.9 (latest available, released September 2025)
  • Configuration Parameter 5 (Initial Dim Level): set to “Last dim level” (default)
  • ZW6HD does not advertise Basic CC or Binary Switch CC, only Multilevel Switch CC

Question for the community

The spec is clear that 0x00 MUST be treated as a state control command, not a level assignment. The switch correctly honors this when HA sends 0xFF to turn back on, restoring the prior level. But it does not honor it when the physical paddle is used to turn back on after receiving 0x00 from Z-Wave JS.

This leaves an open question: is there a way for Z-Wave JS or HA to send the off command in a way that the switch’s internal paddle restore path treats identically to a local wall off? Other manufacturers’ dimmers apparently do not exhibit this split behavior. Has anyone seen this specific issue with Leviton dimmers, and is there a known workaround at the Z-Wave JS or HA configuration level?

I have only one Leviton ZW6HD, which I’ve bought out of curiosity. All my other wall switches are Zooz. They offer many options and work well. The Leviton ZW6WD one offers just few options and my unit never returns to its previous dim level. I use mostly its paddle and, after turning it on, it always goes back to 99%.

Hmm…. all 67 of my units from paddle always go back to their previous DIM level.
That maybe could indicate that your configuration is set to go that level VS previous setting.

I wonder what I'm missing. Here's my configuration.

try this test: (all from switch, nothing from HA)

  1. Turn on from switch
  2. Set dim to 50%
  3. Turn off
  4. Turn on

Also reminder: if you double tap to turn on, it will always go back to 100%.
Single Tap takes to last level
Double Tap overrides and goes to 100%

Works in reverse too, where you double tap off, and it is instant off VS slowly turning it off.

Checking in with any Gurus here :slight_smile: ...
Any way we can solve this?
Wondering if HA is sending off in a different manner than how you turn it off manually?
OR do we need to ask Leviton to update the firmware?

If latter, can anyone help with that?

You could create a workaround with an automation and some templates.

First setup a triggered template to capture the dim value. Maybe ignore it going to 1. Probably triggered on the light entity dim attribute.

Create an automation that detects the switch being turn on from the wall (hint listen to the zwave scene control events) and adjust the brightness to the value stored in the template.

Thanks Pete, yes that was along the lines I was thinking. Can be done.
Just was trying to find a more holistic approach via HA or Firmware reporting to Leviton (if its on their side).

But I'll work on that template and triggers to see if that can be done. So many edge cases come to mind.

It appears that HA / zwavejs is doing the right thing. You could certainly verify this by asking a question on zwavejs Sign in to GitHub · GitHub - if they verify its a device issue then at least its documented on the zwavejs site for other to find. If it's a device issue, you may want to report it to Leviton, let them know you have hundreds of devices and point them to that discussion that shows it's a bug. If that report makes it to their product manager they may device to fix it.

Thanks, yes.
With 67 devices, I tried that solution (recording state and then remembering) before coming to this thread (pretty good with scripting) and it works, but adds way too much zwave traffic. And then there are edge cases. I know I can work through those edge cases.

But yes, I'll go to zwavejs github to report this over there too.

it would add one command when the scene event gets delivered and the automation set the dim level, and that only happen when a person turns the light on manually

hahah, you'd think :slight_smile: ... I've got a few more people in my home, and lights turn on off / get dimmed all the time :slight_smile: