Support of OTA firmware updates for Z-Wave / Z-Wave Plus devices

@Keo

I've done something similar to update Aeon/Aeotec devices. They supply packages to do the updates that use the Zensys tools as the base. I have several "spare" ZSticks and one of them is joined to the Hubitat network as a Secondary. I plug that into my Mac, fire up my virtual Windows PC and run the Aeon software. I've updated Multisenor6's, Minimotes and Wallmotes this way.

I do not have to Exclude the devices before starting the update. I have not run into it, but I can imagine that leaving it as an active device while doing the update could, under just the right circumstance, fail. So far, I haven't run into it and I performed one of these updates as recently as last week.

From March 2018:

I like the Aeon ZStick because it has a battery and a button. The battery means it works when not plugged into USB power. And the button puts it into Include or Exclude mode. For pretty much the same $ you'd pay for any ZWave USB stick, the extra feature is worth it, to me.

The problem is the Z-Flash software is Windows only, there may be an alternative but I am not sure. You could also just spin up a Windows VM temporary.

I think the Hubitat stick will work but I haven't tried using it away from the main device. I have a Z-stick from Aeotec which is useful for getting information about Z-wave nodes, pairing devices etc.

This can be recognized as a secondary Z-wave controller and as such can access the information about the primary Z-wave network.

Very handy. Have'nt explored how well Hubitat handles primary/secondary controllers but I suspect it will be more compliant than Smartthings (which had idiosyncracies that made it of limited use) others may know more.

The Hubitat stick will work I used it and was successful. All Z-Wave information is saved to the stick.

I actually bought the HomeSeer kit, but still wanted to see if it would work with Hubitat's USB. Going forward I would just use the HomeSeer USB, so I do not need to take the hub offline.

If anyone is looking for a good driver for the Leviton Dimmer's the one linked below is what I use and you can configured all the advanced functions.

https://community.hubitat.com/t/leviton-z-wave-plus-dimmers-firmware-update-may-needed-to-work-with-generic-smart-driver/4442/2?u=raidflex

1 Like

Continuing the discussion from Rule Help - Persistent light state option with motion controller:

This merits a new topic that I hope others will find useful if they have Levition Plus Z-Wave Dimmers and Switches that don't behave nicely when they transfer them, I suspect there will still be versions for sale that haven't been updated to the new firmware and for those if us who were early adopters we seem to have an additional challenge to get them to work. I still find them a great dimmer/switch ... and when the update is applied the response to a change is lightening fast on a dashboard.

[Rule Help - Persistent light state option with motion controller]

This has solved my problem - the Leviton Z-wave plus dimmers (**Models DZD6HD and DZ1KD ) were being recognized by Hubitat correctly and the Generic Smart Zwave Dimmer was being applied. Unfortunately this didn't work with the Dimmers which were not reporting a physical or digital change, although the device would switch on and off and dim.

I emailed support and got promptly referred to this topic referencing the updated z-wave firmware for these dimmers. I have about 50 of these, which I installed last year using Smartthings generic device drivers. They have always functioned flawlessly and I never saw the need to change the firmware.

Using the generic "non smart" driver I could get them to function but when I tried to modify the custom device handler from Smartthings community it wouldn't work either and I was hoping to be able to use this to set some of the parameters for fade time etc. without using the physical switch.

Using the links provided above I had to download the Z-flash utility from HomeSeer and connected this as a secondary controller to my Hubitat, after installing the driver on a windows 10 PC.

The software found the devices I had transferred and correctly identified the firmware versions and allowed me to update the dimmers. (This is painfully slow - 10 minutes or more per device and it fails if the dimmer is switched during the process). You can set the software to update the same models sequentially and leave it running.

This has solved the problem. The devices are now responding quickly and updating the status within seconds. Just the way it should. The modified Smartthings driver also seems to work but it switches the dimmers to zero when they are switched rather than leaving them at the last l (on the dashboard).

So now I can get on with the rest of the whole house transfer. So far I've had to change a couple of custom divers to support a Z-wave outlet with power management. Things seem to be responding very quickly and the web interface is much better than using a phone/table to configure settings.

Good so far - will update as I make further progress (am updating the remaining firmwares while they are still connected to the Smartthings hub at present).

Reply

Bookmark Share Flag Invite Reply

Tracking

You will see a count of new replies because you posted a reply to this topic.

Suggested Topics

There are 5 unread and 9 new topics remaining, or browse other topics in Automations

Here is a good driver to use that has basically all advanced functions for the Leviton dimmers.

/**
 *  Copyright 2017 Jason Xia
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
metadata {
    definition (name: "Leviton Decora Z-Wave Plus Dimmer", namespace: "jasonxh", author: "Jason Xia", ocfDeviceType: "oic.d.light") {
        capability "Actuator"
        capability "Configuration"
        //capability "Health Check"
        capability "Indicator"
        capability "Light"
        capability "Polling"
        capability "Refresh"
        capability "Sensor"
        capability "Switch"
        capability "Switch Level"

        attribute "loadType", "enum", ["incandescent", "led", "cfl"]
        attribute "presetLevel", "number"
        attribute "minLevel", "number"
        attribute "maxLevel", "number"
        attribute "fadeOnTime", "number"
        attribute "fadeOffTime", "number"
        attribute "levelIndicatorTimeout", "number"

        command "low"
        command "medium"
        command "high"
        command "levelUp"
        command "levelDown"

        fingerprint mfr:"001D", prod:"3201", model:"0001", deviceJoinName: "Leviton Decora Z-Wave Plus 600W Dimmer"
        fingerprint mfr:"001D", prod:"3301", model:"0001", deviceJoinName: "Leviton Decora Z-Wave Plus 1000W Dimmer"
    }

    preferences {
        input name: "levelIncrement", type: "number", title: "In-App Level Increment",
                description: "1 - 100 (default $defaultLevelIncrement)", range: "1..100", defaultValue: defaultLevelIncrement,
                displayDuringSetup: false, required: false

        input type: "paragraph", element: "paragraph", title: "Device Preferences",
                description: "The following preferences are configuring the device behaviors. " +
                        "All of them are optional. Leave a preference empty to skip configuring it."

        input name: "loadType", type: "enum", title: "Load type",
                options: ["Incandescent (default)", "LED", "CFL"],
                displayDuringSetup: false, required: false
        input name: "indicatorStatus", type: "enum", title: "Indicator LED is lit",
                options: ["When switch is off (default)", "When switch is on", "Never"],
                displayDuringSetup: false, required: false
        input name: "presetLevel", type: "number", title: "Light turns on to level",
                description: "0 = last dim level (default)\n1 - 100 = fixed level", range: "0..100",
                displayDuringSetup: false, required: false
        input name: "minLevel", type: "number", title: "Minimum light level",
                description: "0 to 100 (default 10)", range: "0..100",
                displayDuringSetup: false, required: false
        input name: "maxLevel", type: "number", title: "Maximum light level",
                description: "0 to 100 (default 100)", range: "0..100",
                displayDuringSetup: false, required: false
        input name: "fadeOnTime", type: "number", title: "Fade-on time",
                description: "0 = instant on\n1 - 127 = 1 - 127 seconds (default 2)\n128 - 253 = 1 - 126 minutes", range: "0..253",
                displayDuringSetup: false, required: false
        input name: "fadeOffTime", type: "number", title: "Fade-off time",
                description: "0 = instant off\n1 - 127 = 1 - 127 seconds (default 2)\n128 - 253 = 1 - 126 minutes", range: "0..253",
                displayDuringSetup: false, required: false
        input name: "levelIndicatorTimeout", type: "number", title: "Dim level indicator timeout",
                description: "0 = dim level indicator off\n1 - 254 = timeout in seconds (default 3)\n255 = dim level indicator always on", range: "0..255",
                displayDuringSetup: false, required: false
    }
}

def installed() {
    log.debug "installed..."
    initialize()
    response(refresh())
}

def updated() {
    if (state.lastUpdatedAt != null && state.lastUpdatedAt >= now() - 1000) {
        log.debug "ignoring double updated"
        return
    }
    log.debug "updated..."
    state.lastUpdatedAt = now()

    initialize()
    response(configure())
}

def configure() {
    def commands = []
    if (loadType != null) {
        commands.addAll(setLoadType(loadType))
    }
    if (indicatorStatus != null) {
        commands.addAll(setIndicatorStatus(indicatorStatus))
    }
    if (presetLevel != null) {
        commands.addAll(setPresetLevel(presetLevel as short))
    }
    if (minLevel != null) {
        commands.addAll(setMinLevel(minLevel as short))
    }
    if (maxLevel != null) {
        commands.addAll(setMaxLevel(maxLevel as short))
    }
    if (fadeOnTime != null) {
        commands.addAll(setFadeOnTime(fadeOnTime as short))
    }
    if (fadeOffTime != null) {
        commands.addAll(setFadeOffTime(fadeOffTime as short))
    }
    if (levelIndicatorTimeout != null) {
        commands.addAll(setLevelIndicatorTimeout(levelIndicatorTimeout as short))
    }
    log.debug "Configuring with commands $commands"
    commands
}

def parse(String description) {
    def result = null
    def cmd = zwave.parse(description, [0x20: 1, 0x25:1, 0x26: 1, 0x70: 1, 0x72: 2])
    if (cmd) {
        result = zwaveEvent(cmd)
        log.debug "Parsed $cmd to $result"
    } else {
        log.debug "Non-parsed event: $description"
    }
    result
}

def on() {
    def fadeOnTime = device.currentValue("fadeOnTime")
    def presetLevel = device.currentValue("presetLevel")

    short duration = fadeOnTime == null ? 255 : fadeOnTime
    short level = presetLevel == null || presetLevel == 0 ? 0xFF : toZwaveLevel(presetLevel as short)
    delayBetween([
            zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: duration).format(),
            zwave.switchMultilevelV1.switchMultilevelGet().format()
    ], durationToSeconds(duration) * 1000 + commandDelayMs)
}

def off() {
    def fadeOffTime = device.currentValue("fadeOffTime")

    short duration = fadeOffTime == null ? 255 : fadeOffTime
    delayBetween([
            zwave.switchMultilevelV2.switchMultilevelSet(value: 0x00, dimmingDuration: duration).format(),
            zwave.switchMultilevelV1.switchMultilevelGet().format()
    ], durationToSeconds(duration) * 1000 + commandDelayMs)
}

def setLevel(value, durationSeconds = null) {
    log.debug "setLevel >> value: $value, durationSeconds: $durationSeconds"
    short level = toDisplayLevel(value as short)
    short dimmingDuration = durationSeconds == null ? 255 : secondsToDuration(durationSeconds as int)

    sendEvent(name: "level", value: level, unit: "%")
    sendEvent(name: "switch", value: level > 0 ? "on" : "off")
    delayBetween([
            zwave.switchMultilevelV2.switchMultilevelSet(value: toZwaveLevel(level), dimmingDuration: dimmingDuration).format(),
            zwave.switchMultilevelV1.switchMultilevelGet().format()
    ], durationToSeconds(dimmingDuration) * 1000 + commandDelayMs)
}

def poll() {
    delayBetween(statusCommands, commandDelayMs)
}

def ping() {
    poll()
}

def refresh() {
    def commands = statusCommands
    if (getDataValue("MSR") == null) {
        commands << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
    }
    for (i in 1..8) {
        commands << zwave.configurationV1.configurationGet(parameterNumber: i).format()
    }
    log.debug "Refreshing with commands $commands"
    delayBetween(commands, commandDelayMs)
}

def indicatorNever() {
    sendEvent(name: "indicatorStatus", value: "never")
    configurationCommand(7, 0)
}

def indicatorWhenOff() {
    sendEvent(name: "indicatorStatus", value: "when off")
    configurationCommand(7, 255)
}

def indicatorWhenOn() {
    sendEvent(name: "indicatorStatus", value: "when on")
    configurationCommand(7, 254)
}

def low() {
    setLevel(10)
}

def medium() {
    setLevel(50)
}

def high() {
    setLevel(100)
}

def levelUp() {
    setLevel(device.currentValue("level") + (levelIncrement ?: defaultLevelIncrement))
}

def levelDown() {
    setLevel(device.currentValue("level") - (levelIncrement ?: defaultLevelIncrement))
}


private static int getCommandDelayMs() { 1000 }
private static int getDefaultLevelIncrement() { 10 }

private initialize() {
    // Device-Watch simply pings if no device events received for 32min(checkInterval)
    //sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}

private zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd) {
    dimmerEvent(cmd.value)
}

private zwaveEvent(hubitat.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) {
    dimmerEvent(cmd.value)
}

private zwaveEvent(hubitat.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) {
    response(zwave.switchMultilevelV1.switchMultilevelGet().format())
}

private zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
    if (cmd.value == 0) {
        switchEvent(false)
    } else if (cmd.value == 255) {
        switchEvent(true)
    } else {
        log.debug "Bad switch value $cmd.value"
    }
}

private zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) {
    def result = null
    switch (cmd.parameterNumber) {
        case 1:
            result = createEvent(name: "fadeOnTime", value: cmd.configurationValue[0])
            break
        case 2:
            result = createEvent(name: "fadeOffTime", value: cmd.configurationValue[0])
            break
        case 3:
            result = createEvent(name: "minLevel", value: cmd.configurationValue[0])
            break
        case 4:
            result = createEvent(name: "maxLevel", value: cmd.configurationValue[0])
            break
        case 5:
            result = createEvent(name: "presetLevel", value: cmd.configurationValue[0])
            break
        case 6:
            result = createEvent(name: "levelIndicatorTimeout", value: cmd.configurationValue[0])
            break
        case 7:
            def value = null
            switch (cmd.configurationValue[0]) {
                case 0: value = "never"; break
                case 254: value = "when on"; break
                case 255: value = "when off"; break
            }
            result = createEvent(name: "indicatorStatus", value: value)
            break
        case 8:
            def value = null
            switch (cmd.configurationValue[0]) {
                case 0: value = "incandescent"; break
                case 1: value = "led"; break
                case 2: value = "cfl"; break
            }
            result = createEvent(name: "loadType", value: value)
            break
    }
    result
}

private zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
    log.debug "manufacturerId:   $cmd.manufacturerId"
    log.debug "manufacturerName: $cmd.manufacturerName"
    log.debug "productId:        $cmd.productId"
    log.debug "productTypeId:    $cmd.productTypeId"
    def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
    updateDataValue("MSR", msr)
    updateDataValue("manufacturer", cmd.manufacturerName)
    createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
}

private zwaveEvent(hubitat.zwave.commands.hailv1.Hail cmd) {
    createEvent(name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false)
}

private zwaveEvent(hubitat.zwave.Command cmd) {
    log.warn "Unhandled zwave command $cmd"
}

private dimmerEvent(short level) {
    def result = null
    if (level == 0) {
        result = [createEvent(name: "level", value: 0, unit: "%"), switchEvent(false)]
    } else if (level >= 1 && level <= 100) {
        result = createEvent(name: "level", value: toDisplayLevel(level), unit: "%")
        if (device.currentValue("switch") != "on") {
            // Don't blindly trust level. Explicitly request on/off status.
            result = [result, response(zwave.switchBinaryV1.switchBinaryGet().format())]
        }
    } else {
        log.debug "Bad dimming level $level"
    }
    result
}

private switchEvent(boolean on) {
    createEvent(name: "switch", value: on ? "on" : "off")
}

private getStatusCommands() {
    [
            // Even though SwitchBinary is not advertised by this device, it seems to be the only way to assess its true
            // on/off status.
            zwave.switchBinaryV1.switchBinaryGet().format(),
            zwave.switchMultilevelV1.switchMultilevelGet().format()
    ]
}

private short toDisplayLevel(short level) {
    level = Math.max(0, Math.min(100, level))
    (level == (short) 99) ? 100 : level
}

private short toZwaveLevel(short level) {
    Math.max(0, Math.min(99, level))
}

private int durationToSeconds(short duration) {
    if (duration >= 0 && duration <= 127) {
        duration
    } else if (duration >= 128 && duration <= 254) {
        (duration - 127) * 60
    } else if (duration == 255) {
        2   // factory default
    } else {
        log.error "Bad duration $duration"
        0
    }
}

private short secondsToDuration(int seconds) {
    if (seconds >= 0 && seconds <= 127) {
        seconds
    } else if (seconds >= 128 && seconds <= 127 * 60) {
        127 + Math.round(seconds / 60)
    } else {
        log.error "Bad seconds $seconds"
        255
    }
}

private configurationCommand(param, value) {
    param = param as short
    value = value as short
    delayBetween([
            zwave.configurationV1.configurationSet(parameterNumber: param, configurationValue: [value]).format(),
            zwave.configurationV1.configurationGet(parameterNumber: param).format()
    ], commandDelayMs)
}

private setFadeOnTime(short time) {
    sendEvent(name: "fadeOnTime", value: time)
    configurationCommand(1, time)
}

private setFadeOffTime(short time) {
    sendEvent(name: "fadeOffTime", value: time)
    configurationCommand(2, time)
}

private setMinLevel(short level) {
    sendEvent(name: "minLevel", value: level)
    configurationCommand(3, level)
}

private setMaxLevel(short level) {
    sendEvent(name: "maxLevel", value: level)
    configurationCommand(4, level)
}

private setPresetLevel(short level) {
    sendEvent(name: "presetLevel", value: level)
    configurationCommand(5, level)
}

private setLevelIndicatorTimeout(short timeout) {
    sendEvent(name: "levelIndicatorTimeout", value: timeout)
    configurationCommand(6, timeout)
}

private setLoadType(String loadType) {
    switch (loadType) {
        case "Incandescent (default)":
            sendEvent(name: "loadType", value: "incandescent")
            return configurationCommand(8, 0)
        case "LED":
            sendEvent(name: "loadType", value: "led")
            return configurationCommand(8, 1)
        case "CFL":
            sendEvent(name: "loadType", value: "cfl")
            return configurationCommand(8, 2)
    }
}

private setIndicatorStatus(String status) {
    switch (indicatorStatus) {
        case "When switch is off (default)":    return indicatorWhenOff()
        case "When switch is on":               return indicatorWhenOn()
        case "Never":                           return indicatorNever()
    }
}
3 Likes

Thanks - I will give this a try.:grinning:

interestingly this driver also seems to work well with a GE dimmer switch that i tried it with.

have had this one installed for a while now … so not sure how to look up the model # of this device.

Hmmm - I downloaded the Win10 drivers, extracted them etc. When I insert my Hubitat stick, Win10 detects and installs 2 devices (HubZ ZigBee Com Port and HubZ Z-Wave Com Port). But Win10 did not install any drivers (which is what I read/expected). So I go into Device Manager and try to Update Driver for the HubZ Z-Wave Com Port device, but it does not find anything compatible in the extracted files. Any suggestions? Maybe there's some other device that gets updated rather than these 2 that I listed?
Tks,
Mike

If I manually run the 64 bit installer, it installs 7 different Models of devices from Silicon Labs:
Silicon Labs CP210x USB to UART Bridge
Silicon Labs Dual CP2105 USB to UART Bridge: Enhanced COM Port
Silicon Labs Dual CP2105 USB to UART Bridge: Standard COM Port
Silicon Labs Quad CP2108 USB to UART Bridge: Interface0
Silicon Labs Quad CP2108 USB to UART Bridge: Interface1
Silicon Labs Quad CP2108 USB to UART Bridge: Interface2
Silicon Labs Quad CP2108 USB to UART Bridge: Interface3

Not sure which one to force Win10 to use for the Hubitat stick?

Ok - success! Here's some addl info for others wanting to do this firmware upgrade:

1- download the appropriate Windows driver for Hubitat stick from link Raidflex listed earlier. In my case the Win10 link...

2 - extract the driver .zip file and run the appropriate installer app. In my case the 64 bit one named CP210xVCPInstaller_x64

3 - although Windows detects the Hubitat stick upon insertion, it does not find/assign any drivers to the 2 devices (listed in earlier post above)

4 - go into Device Manager (must be an admin) and navigate to Other Devices. You should see 2 devices that were added for the Hubitat stick: HubZ ZigBee Com Port and HubZ Z-Wave Com Port. We're interested in the 2nd one with Z-Wave. Rgt-click and choose Update Driver. Then choose Browse my computer..., then choose Let me pick from a list of available drivers on my computer. Navigate down to Ports (COM & LPT), click [Next], in the Manufacturer list find Silicon Labs, and in the Model list highlight Silicon Labs CP210x USB to UART Bridge. Tell it to install it, and ignore any compatibility warnings.

5 - now that Hubitat's stick driver is installed, you can run HomeSeer's Z-Flash utility. Once Z-Flash loads, it will have drop downs in upper left for Interface and Com Port. Choose USB Z-Wave Interface. In my case there was only one option for Com Port (COM3, but yours might be different?). Once those 2 items are selected, there is an [Enable] button just below the Interface selection, click it. That should populate the area titled "Step 2 Device Management" and list the Leviton dimmer. Put a checkmark next to dimmer in that list, then - if all the planets are aligned - you can click on Step 4's [Start Update] button. Note - you might have to first choose the appropriate file in Step 3 Select the Update File. Mine had only one choice - the version 1.20 file...

Another note: I did this on Windows 10 Professional running under Parallels on my iMac. Don't know if the iMac was at fault, but I did have to repeat Step 4 three times before it successfully updated. The first two times it got to almost 80 or 90% done (7 to 8 minutes), then reported "Device reports unable to receive, aborting".

Happy updating :slight_smile:

4 Likes

Just tried this on 2 different PCs. Forced the driver install, opened Z-Flash, selected the USB stick on Com3, clicked enabled - and nothing populates.

You have to add it to the He network. Go into HE and select discover devices. Then on your stick put it into pairing mode. Then your entire network of devices will show up and you can do whatever you want with it.

Sorry, I should have mentioned that I am using the HE zwave USB stick.

You should see the devices then.

For those that added a Z-Stick as a secondary controller to Hubitat... 2 questions:

  1. Do you leave it included in the Hubitat network at all times, or do you exclude it when done? If you leave it included, but powered down, does that cause issues?

  2. To include it as a secondary controller, did you just use the Zensys Tool and put it in learning mode, and then include on Hubitat? Or other?

Thanks.

  1. I leave it included. Powered down doesn't matter. Secondaries are not expected to be Fixed location in the Mesh.

  2. I personally use OZWCP for the include, but as luck would have it, I did one using Zensys Tools last week. Yes, you enter Learn on Zensys and then Discover Devices on Hubitat.

I have two hubs and decided to backup the 2nd stick... I had just been updating some Aeon devices and therefore was already in Windows / Zensys Tools. Would have taken a whole 8 mins more to switch to OZWCP :smiley::smiley:

1 Like

Does anyone know where I can find the latest firmware for DZ6HD-1BZ?

I got my zstick added as secondary last night. Pretty simple - thanks!

I didn't have a lot of time to look, but there were a few interesting/unexpected things in the routing info in Zensys that I need to think on... Surprising considering how many zwave plus hard wired devices I have.

I'm not having any zwave issues, so it may be academic, but it is interesting nonetheless.

1 Like