Physical Events Not Logging Consistently

OK, so this thread’s mostly about Z-Wave switches, but I’m going to sneak a lock question in here. @mike.maxwell I installed a YRD256 last night, all seems good but just like @cwwilson08 found, I have to manually refresh. Deadbolt operation via RM isn’t a problem for me, but manual operation from HomeKit via Homebridge is.

I’ve added a button to HomeKit to manually refresh but when I try to automatically refresh based on the lock opening or closing, it doesn’t seem to work consistently. Is this due to a lazy refresh from Z-Wave locks in general? Is it to save battery life?

I could set up a periodic refresh at 10 second intervals, but I don’t want to hit the lock too much because I am concerned that will drain batteries. Is this a valid concern?

Also, despite the title of this thread, the lock state does not automatically update in the driver (Generic Z-Wave Lock), even when the operation is via a digital event.

Nope! This is a completely unacceptable answer. How could that ever be an acceptable support answer?

I have at least 20 GE on/off switches that do not register state when using the physical switch (one closest to me is ZW4005 - 1513 on switch). I tried to set up the polling app and even tried to create a rule. Both did not properly update the status of the switches. Now I have 20+ switches that need to be removed, excluded and added back to Smartthings. I really wanted to like this platform, but I am not about to spend $800+ to update switches that work (albeit through frequent polling on Smartthings).

Does it work if you manually run the poll or refresh action on the device/details page? If so, there must be a problem with the polling app or rule. But if the manual poll or refresh does not work, the automatic one will never work, either. (I've never heard of a manual poll action not working, but the old GE switches are odd and not everyone's experience is the same with them, so who knows.)

You might be able to get some help troubleshooting this problem instead of needing to migrate them back to a different controller, with the exclusion process also reported to be somewhat of a pain on these things, too. :slight_smile:

I appreciate the response. I was able to force remove the devices from Hubitat, ran the general exclusion in ST, and easily added them back to ST. This is all disappointing. I spent a good part of a day moving things over only to find out that many “older” GE switches do not work. The solution is to buy better switches...awesome. These switches have worked since the beginning with ST. Now they are a problem with a new platform?!?!? I don’t get it.

I will say that I'm very disappointed with GE switches and dimmers performance on HE, fortunately I just have 4 more to replace, including one dimmer that it is z wave plus version but still giving me inconsistent reports, I have everything else zooz and they work fine.

The GE plus dimmer I had to change the driver to the non smart to add it to the poller with the other 3 switches and one old eaton receptacle that I will replace too.

I have a house full of old GE switches. Poller never failed to update the status correctly, and the Refresh button has also always worked consistently. Sounds like there is something unique in your setup, either a hardware problem, or something wrong with how you set up the apps/rules. Make a post detailing the problems you are seeing, the community is great about helping with this kind of stuff. Hard to troubleshoot when nobody knows you are having a problem.

2 Likes

Yup, weird.

I exclusively have GE switches/dimmers, and all work 100% 100% of the time.

They are all zwave plus, so I don't use poller and can't comment there. I have some 14291 switch, some 14287 fan control. some 14294 dimmer, some 26931 motion switch, some 26933 motion dimmers, and a few 14280 dual outlet dimmer modules.

I REALLY like the new motion detecting switch/dimmer and am replacing a number of my other switch/dimmers with that model.

I guess no device is 100% for everyone, or it would be the only device in the marketplace. :slight_smile:

Do the devices you are removing need a new home? :grin:

A few of the displaced units I am putting in locations I hadn't planned on automating (because, why not if I have an extra switch/dimmer). But I may have a few extras that I'll part with in the end.

I'll give y'all first dibs. :slight_smile:

1 Like

Can someone else that has a Leviton DZ6HD dimmer check thier log to see if physical events are reported? This was working for me and recently I noticed all events are being registered as digital. I do have one GE dimmer that still seems to be registering physical events.

If I use a custom driver I do see a "hail" command when physically turning on/off the switch, but with the stock "Generic Z-Wave smart dimmer" it's always digital.

I have also tried to use "configure" and "refesh" under the device page with no success. I have replicated this issue with multiple dimmers.

All dimmers have latest firmware v1.2.

I have six of these dimmers and they do show physical events using the Generic Z-Wave Smart Dimmer driver. They are all on the latest v1.2 firmware.

The driver suppresses the Hail() messages by default, but you can see them if you enable debug logging from the device page.

Can you try to exclude and re-include the devices (and possibly pull the air-gap on them)? I suspect if they don't include correctly, the built-in driver does not enable the device-specific code needed to handle them.

Here's my experience when migrating from Smartthings. Some of the dimmers would show physical events and some would not. The devices which behaved correctly had a slightly different fingerprint than those that did not. I was able to fix the issue by excluding and re-including the "bad" devices, at which time they showed the correct fingerprint.

I captured the fingerprints in case I needed to contact support:

Correct -- reports physical events:

Incorrect -- does not report physical events:

The difference is in the MSR (manufacturer specific report) and the manufacturer. Interestingly the incorrect case shows Leviton as the manufacturer -- which is the opposite of what one would expect.

Thanks, this was the issue and my finger prints were set to the incorrect one. The big question is why did this change? Mine were all correct and working and then at some point changed. Literally every single dimmer is messed up. I am now going to have to exclude/re-include over 30 devices.

You should reach out to Support or maybe @mike.maxwell can comment because changing driver, save, and clicking configure should do the same as exclude and re-include.

1 Like

Yeah I may create a ticket, or @mike.maxwell might have some input. I would like to avoid having to re-pair all those devices, because then I have to add the device back to all of my automation.

This was the custom driver that I used that does show the hail commands. I mainly used this driver in the past to configure the dimmer's settings, since it made it much easier then doing it at the switch itself.

/**
 *  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"
		attribute "firmwareVersion", "string"

        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 to 100 (default 0)", range: "0..100",
                displayDuringSetup: false, required: false
        input type: "paragraph", element: "paragraph", title: "",
                description: "0 = last dim level (default)\n1 - 100 = fixed level"
        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 to 253 (default 2)", range: "0..253",
                displayDuringSetup: false, required: false
        input type: "paragraph", element: "paragraph", title: "",
                description: "0 = instant on\n1 - 127 = 1 - 127 seconds (default 2)\n128 - 253 = 1 - 126 minutes"
        input name: "fadeOffTime", type: "number", title: "Fade-off time",
                description: "0 to 253 (default 2)", range: "0..253",
                displayDuringSetup: false, required: false
        input type: "paragraph", element: "paragraph", title: "",
                description: "0 = instant off\n1 - 127 = 1 - 127 seconds (default 2)\n128 - 253 = 1 - 126 minutes"
        input name: "levelIndicatorTimeout", type: "number", title: "Dim level indicator timeout",
                description: "0 to 255 (default 3)", range: "0..255",
                displayDuringSetup: false, required: false
        input type: "paragraph", element: "paragraph", title: "",
                description: "0 = dim level indicator off\n1 - 254 = timeout in seconds (default 3)\n255 = dim level indicator always on"
    }
}

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
    commands << zwave.versionV1.versionGet().format()

    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, offlinePingable: "1"])
}

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.commands.versionv1.VersionReport cmd) {
    createEvent(name: "firmwareVersion", value: "${cmd.applicationVersion}.${cmd.applicationSubVersion}", 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 (!isNewFirmware && device.currentValue("switch") != "on") {
            // Don't blindly trust level. Explicitly request on/off status.
            result << response(zwave.switchBinaryV1.switchBinaryGet().format())
        } else {
            result << switchEvent(true)
        }
    } else {
        log.debug "Bad dimming level $level"
    }
    result
}

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

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

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()
    }
}

private boolean getIsNewFirmware() { device.currentValue("firmwareVersion") ==~ /1\.2\d/ }

Yes, I use that custom driver for the same reason (ease of configuration). It works well enough except for physical / digital reporting.

It's disconcerting that your switches which used to work correctly now don't.

If you parse the MSR on the "wrong" fingerprint, it is actually correct per the Z-wave product catalog entry:
Manufacturer ID = 0x001D is Leviton (decimal 29)
Product Type ID: 0x3201
Product ID: 0x0001

So I'm not sure why the fingerprint is being interpreted incorrectly / differently.

So I think I found the issue. If I pair the device initially it chooses the "Generic Z-Wave Smart Dimmer" driver which is correct. If I then switch to the custom driver to apply any settings changes to the switch, after I "configure" and "refresh" the device the Manufacturer ID changes from "29" to "Leviton". Even after I switch back to the stock driver and use the "configure" and "refresh" options, the manufacture stays on "Leviton" and sure enough the physical events are not logged anymore.

This would explain why I really did not notice a problem because over time I would have tweaked some of the dimmer settings with the custom driver.

There was bug fixes in the 2.0.4 update concerning Z-Wave but those may be unrelated, or possibly a previous update. I know in the past I have used the custom driver and even after that physical events were still working.

It sounds like the "Generic Z-Wave Smart Dimmer" driver may need to be updated, at least according to what you have found regarding the "wrong" fingerprint actually being the correct one.

I can confirm the issue.

I switched to the custom driver, made a change, configured, refreshed, switched back to the generic driver. And sure enough, the fingerprint changed per your observations.

This is a dimmer I added just 2 days ago, while the hub was still on 2.03 firmware. At that time I was able to switch to the custom driver, configure the settings, and switch back to the generic driver with no ill effects. So this issue is definitely related to the 2.04 update.

Thanks for confirming the problem also. I will reach out to @mike.maxwell or create a support ticket if necessary.

This seems similar to a problem I created a ticket for last night. @bobbyD has seen the phenomenon and we'd like @chuck.schwer to look at it but as luck would have it, he's got the flu. :frowning:

1 Like

Glad I am not the only one. I hope he has a speedy recovery, flu is not fun.

1 Like