[RELEASE] Inovelli Device Drivers

@ericm
Is there a way to turn off the debug logging from the NZW37?
As you can see by the attached screenshot, the device is flooding my log:
(this is only the first page of MANY pages of logs!!!)

The driver does not actually have a parameter to control the logs on/off like most do, nor does it appear to time out after X amount of time. So the short answer is, you cannot turn it off with the driver as it stands.

You could make changes to the driver though in order to add that capability. Mine does not seem to be having nearly as much activity as yours does though. Do you have a Rule or something that might be triggering the device? Because the log looks like it is getting "on" commands over and over.

I think that the easiest approach is for @ericm to modify the device handler and make info/device logging an option (the same way it is done for most other device handlers).

That's certainly a good idea, but if all these events are being generated by the device itself, that looks like it has the potential to absolutely flood your Z-Wave network and be a good idea to fix on the device or firmware side regardless of logging. Just something else for them to look into if it's not caused by a rule or other automation on the poster's side repeatedly doing something like turning the device on. :slight_smile:

Here is a revised version of the driver. I added a simple "DebugOn" boolean in the preferences and then if( DebugOn){ ... } at every log.debug instance. I changed the namespace and author to me JUST to help distinguish this is not the "legitimate" Inovelli driver. Hopefully it helps in the meantime. I noticed when testing with mine that the driver is very verbose when logging... something Inovelli might want to look into a bit when cleaning up.

/**
 *
 * NOTE from David Snell: I am in no way associated with Inovelli. This was provided ONLY so that people could turn off debug logging.
 *  This was accomplished by adding a DebugOn preference that is used by an if( DebugOn){ ... } at every referenced log.debug
 *  No other code was modified by me, although I had to change namespace and author obviously so there would be no confusion with the
 *  "real" driver from Inovelli.
 *
 *  Hubitat Import URL: https://raw.githubusercontent.com/InovelliUSA/Hubitat/master/Drivers/inovelli-2-channel-smart-plug-nzw37.src/inovelli-2-channel-smart-plug-nzw37.groovy
 *
 *  Inovelli 2-Channel Smart Plug NZW37
 *   
 *  Original publish on github: Eric Maycock (erocm123)
 *  Original publish on Date: 2018-06-05
 *  Original Copyright Eric Maycock
 *
 *  Includes all configuration parameters and ease of advanced configuration. 
 *
 *  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.
 *
 *  2019-08-20: Adding ability to disable debug statements - via David Snell
 * Inovelli USA Provided Revisions:
 *  2018-06-05: Switching back to child device configuration
 *  2018-04-09: Changed back to use encapsulation commands and removed child device references
 *  2018-03-27: Adapted for Hubitat.
 */

metadata {
    definition(name: "Inovelli 2-Channel Smart Plug NZW37 w/DebugOn", namespace: "Snell", author: "David Snell") {
        capability "Actuator"
        capability "Sensor"
        capability "Switch"
        capability "Polling"
        capability "Refresh"
        capability "Health Check"
        capability "PushableButton"
        capability "Configuration"
        
        attribute "lastActivity", "String"
        attribute "lastEvent", "String"
        
        command "setAssociationGroup", ["number", "enum", "number", "number"] // group number, nodes, action (0 - remove, 1 - add), multi-channel endpoint (optional)
        command "childOn"
        command "childOff"
        command "childRefresh"

        fingerprint mfr: "015D", prod: "0221", model: "251C"
        fingerprint mfr: "0312", prod: "B221", model: "251C"
        fingerprint deviceId: "0x1001", inClusters: "0x5E,0x85,0x59,0x5A,0x72,0x60,0x8E,0x73,0x27,0x25,0x86"
        fingerprint deviceId: "0x1001", inClusters: "0x5E,0x25,0x27,0x85,0x8E,0x59,0x55,0x86,0x72,0x5A,0x73,0x70,0x5B,0x9F,0x60,0x6C,0x7A"
        fingerprint deviceId: "0x1001", inClusters: "0x5E,0x25,0x27,0x85,0x8E,0x59,0x55,0x86,0x72,0x5A,0x73,0x70,0x5B,0x60,0x6C"
    }
    
    simulator {}
    
    preferences {
        input "autoOff1", "number", title: "Auto Off Channel 1\n\nAutomatically turn switch off after this number of seconds\nRange: 0 to 32767", description: "Tap to set", required: false, range: "0..32767"
        input "autoOff2", "number", title: "Auto Off Channel 2\n\nAutomatically turn switch off after this number of seconds\nRange: 0 to 32767", description: "Tap to set", required: false, range: "0..32767"
        input "ledIndicator", "enum", title: "LED Indicator\n\nTurn LED indicator on when switch is:\n", description: "Tap to set", required: false, options:[["0": "On"], ["1": "Off"], ["2": "Disable"]], defaultValue: "0"
        input description: "1 pushed - Button 2x click", title: "Button Mappings", displayDuringSetup: false, type: "paragraph", element: "paragraph"
        input description: "Use the \"Z-Wave Association Tool\" SmartApp to set device associations. (Firmware 1.02+)\n\nGroup 2: Sends on/off commands to associated devices when switch is pressed (BASIC_SET).", title: "Associations", displayDuringSetup: false, type: "paragraph", element: "paragraph"
		input "DebugOn", "bool", title: "Turn debugging on?", required: false, default: false
    }
    
    tiles {
        multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
            tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
                attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
                attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff"
                attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
                attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff"
            }
        }
        
        standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh"
        }
        
        valueTile("lastActivity", "device.lastActivity", inactiveLabel: false, decoration: "flat", width: 4, height: 1) {
            state "default", label: 'Last Activity: ${currentValue}',icon: "st.Health & Wellness.health9"
        }
        
        valueTile("icon", "device.icon", inactiveLabel: false, decoration: "flat", width: 4, height: 1) {
            state "default", label: '', icon: "https://inovelli.com/wp-content/uploads/Device-Handler/Inovelli-Device-Handler-Logo.png"
        }
    }
}
def parse(String description) {
    def result = []
    def cmd = zwave.parse(description)
    if (cmd) {
        result += zwaveEvent(cmd)
        if( DebugOn ){ log.debug "Parsed ${cmd} to ${result.inspect()}" }
    } else {
        if( DebugOn ){ log.debug "Non-parsed event: ${description}" }
    }
    
    def now
    if(location.timeZone)
    now = new Date().format("yyyy MMM dd EEE h:mm:ss a", location.timeZone)
    else
    now = new Date().format("yyyy MMM dd EEE h:mm:ss a")
    sendEvent(name: "lastActivity", value: now, displayed:false)
    
    return result
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd, ep = null) {
    if( DebugOn ){ log.debug "BasicReport ${cmd} - ep ${ep}" }
    if (ep) {
        def event
        childDevices.each {
            childDevice ->
                if (childDevice.deviceNetworkId == "$device.deviceNetworkId-ep$ep") {
                    childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off")
                }
        }
        if (cmd.value) {
            event = [createEvent([name: "switch", value: "on"])]
        } else {
            def allOff = true
            childDevices.each {
                childDevice ->
				    if (childDevice.deviceNetworkId != "$device.deviceNetworkId-ep$ep") 
                       if (childDevice.currentState("switch").value != "off") allOff = false
            }
            if (allOff) {
                event = [createEvent([name: "switch", value: "off"])]
            } else {
                event = [createEvent([name: "switch", value: "on"])]
            }
        }
        return event
    }
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicSet cmd) {
    if( DebugOn ){ log.debug "BasicSet ${cmd}" }
    def result = createEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital")
    def cmds = []
    cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1)
    cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2)
    //return [result, response(commands(cmds))] // returns the result of reponse()
    return response(commands(cmds)) // returns the result of reponse()
}

def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) {
    if( DebugOn ){ log.debug "SwitchBinaryReport ${cmd} - ep ${ep}" }
    if (ep) {
        def event
        def childDevice = childDevices.find {
            it.deviceNetworkId == "$device.deviceNetworkId-ep$ep"
        }
        if (childDevice) childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off")
        if (cmd.value) {
            event = [createEvent([name: "switch", value: "on"])]
        } else {
            def allOff = true
            childDevices.each {
                n->
                    if (n.deviceNetworkId != "$device.deviceNetworkId-ep$ep" && n.currentState("switch").value != "off") allOff = false
            }
            if (allOff) {
                event = [createEvent([name: "switch", value: "off"])]
            } else {
                event = [createEvent([name: "switch", value: "on"])]
            }
        }
        return event
    } else {
        def result = createEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital")
        def cmds = []
        cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1)
        cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2)
        return [result, response(commands(cmds))] // returns the result of reponse()
    }
}

def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
    if( DebugOn ){ log.debug "MultiChannelCmdEncap ${cmd}" }
    def encapsulatedCommand = cmd.encapsulatedCommand([0x32: 3, 0x25: 1, 0x20: 1])
    if (encapsulatedCommand) {
        zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer)
    }
}

def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
    if( DebugOn ){ log.debug "ManufacturerSpecificReport ${cmd}" }
    def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
    if( DebugOn ){ log.debug "msr: $msr" }
    updateDataValue("MSR", msr)
}

def zwaveEvent(hubitat.zwave.commands.centralscenev1.CentralSceneNotification cmd) {
    createEvent(buttonEvent(cmd.sceneNumber, (cmd.sceneNumber == 2? "held" : "pushed"), "physical"))
}

def buttonEvent(button, value, type = "digital") {
    sendEvent(name:"lastEvent", value: "${value != 'pushed'?' Tap '.padRight(button+1+5, '▼'):' Tap '.padRight(button+1+5, '▲')}", displayed:false)
    [name: value, value: button, isStateChange:true]
}

def zwaveEvent(hubitat.zwave.Command cmd) {
    // This will capture any commands not handled by other instances of zwaveEvent
    // and is recommended for development so you can see every command the device sends
    if( DebugOn ){ log.debug "Unhandled Event: ${cmd}" }
}

def on() {
    if( DebugOn ){ log.debug "on()" }
    commands([
            zwave.switchAllV1.switchAllOn(),
            encap(zwave.basicV1.basicGet(), 1),
            encap(zwave.basicV1.basicGet(), 2)
    ])
}

def off() {
    if( DebugOn ){ log.debug "off()" }
    commands([
            zwave.switchAllV1.switchAllOff(),
            encap(zwave.basicV1.basicGet(), 1),
            encap(zwave.basicV1.basicGet(), 2)
    ])
}

def childOn(String dni) {
    if( DebugOn ){ log.debug "childOn($dni)" }
    def cmds = []
    commands([
		encap(zwave.basicV1.basicSet(value: 0xFF), channelNumber(dni)),
        encap(zwave.basicV1.basicGet(), channelNumber(dni))
    ])
}

def childOff(String dni) {
    if( DebugOn ){ log.debug "childOff($dni)" }
    def cmds = []
    commands([
		encap(zwave.basicV1.basicSet(value: 0x00), channelNumber(dni)),
        encap(zwave.basicV1.basicGet(), channelNumber(dni))
    ])
}

def childRefresh(String dni) {
    if( DebugOn ){ log.debug "childRefresh($dni)" }
    def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicGet(), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds
}

def poll() {
    if( DebugOn ){ log.debug "poll()" }
    commands([
            encap(zwave.basicV1.basicGet(), 1),
            encap(zwave.basicV1.basicGet(), 2),
    ])
}

def refresh() {
    if( DebugOn ){ log.debug "refresh()" }
    commands([
            encap(zwave.basicV1.basicGet(), 1),
            encap(zwave.basicV1.basicGet(), 2),
    ])
}

def ping() {
    if( DebugOn ){ log.debug "ping()" }
    refresh()
}

def installed() {
    refresh()
}

def configure() {
    if( DebugOn ){ log.debug "configure()" }
    def cmds = initialize()
    commands(cmds)
}

def updated() {
    if (!state.lastRan || now() >= state.lastRan + 2000) {
        if( DebugOn ){ log.debug "updated()" }
        state.lastRan = now()
        def cmds = initialize()
        commands(cmds)
    } else {
        if( DebugOn ){ log.debug "updated() ran within the last 2 seconds. Skipping execution." }
    }
}

def integer2Cmd(value, size) {
    try{
	switch(size) {
	case 1:
		[value]
    break
	case 2:
    	def short value1   = value & 0xFF
        def short value2 = (value >> 8) & 0xFF
        [value2, value1]
    break
    case 3:
    	def short value1   = value & 0xFF
        def short value2 = (value >> 8) & 0xFF
        def short value3 = (value >> 16) & 0xFF
        [value3, value2, value1]
    break
	case 4:
    	def short value1 = value & 0xFF
        def short value2 = (value >> 8) & 0xFF
        def short value3 = (value >> 16) & 0xFF
        def short value4 = (value >> 24) & 0xFF
		[value4, value3, value2, value1]
	break
	}
    } catch (e) {
        if( DebugOn ){ log.debug "Error: integer2Cmd $e Value: $value" }
    }
}

def initialize() {
    if( DebugOn ){ log.debug "initialize()" }
    if (!childDevices) {
        createChildDevices()
    } else if (device.label != state.oldLabel) {
        childDevices.each {
            if (it.label == "${state.oldLabel} (CH${channelNumber(it.deviceNetworkId)})") {
                def newLabel = "${device.displayName} (CH${channelNumber(it.deviceNetworkId)})"
                it.setLabel(newLabel)
            }
        }
        state.oldLabel = device.label
    }
    sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
    sendEvent(name: "numberOfButtons", value: 1, displayed: true)
    def cmds = processAssociations()
    cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: ledIndicator!=null? ledIndicator.toInteger() : 0, parameterNumber: 1, size: 1)
    cmds << zwave.configurationV1.configurationGet(parameterNumber: 1)
    cmds << zwave.configurationV1.configurationSet(configurationValue: autoOff1!=null? integer2Cmd(autoOff1.toInteger(), 2) : integer2Cmd(0,2), parameterNumber: 2, size: 2)
    cmds << zwave.configurationV1.configurationGet(parameterNumber: 2)
    cmds << zwave.configurationV1.configurationSet(configurationValue: autoOff2!=null? integer2Cmd(autoOff2.toInteger(), 2) : integer2Cmd(0,2), parameterNumber: 3, size: 2)
    cmds << zwave.configurationV1.configurationGet(parameterNumber: 3)
    return cmds
}

def zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport cmd) {
    if( DebugOn ){ log.debug "${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd.configurationValue}'" }
}

private encap(cmd, endpoint) {
    if (endpoint) {
        zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd)
    } else {
        cmd
    }
}

private command(hubitat.zwave.Command cmd) {
    if (state.sec) {
        zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
    } else {
        cmd.format()
    }
}

private commands(commands, delay = 1000) {
    delayBetween(commands.collect {
        command(it)
    }, delay)
}

private channelNumber(String dni) {
    dni.split("-ep")[-1] as Integer
}
private void createChildDevices() {
    state.oldLabel = device.label
    for (i in 1..2) {
        addChildDevice("Switch Child Device", "${device.deviceNetworkId}-ep${i}", [completedSetup: true, label: "${device.displayName} (CH${i})",
            isComponent: false, componentName: "ep$i", componentLabel: "Channel $i"
        ])
    }
}

def setDefaultAssociations() {
    def smartThingsHubID = zwaveHubNodeId.toString().format( '%02x', zwaveHubNodeId )
    state.defaultG1 = [smartThingsHubID]
    state.defaultG2 = [smartThingsHubID]
    state.defaultG3 = []
}

def setAssociationGroup(group, nodes, action, endpoint = null){
	if( DebugOn ){ log.debug nodes }
	if( DebugOn ){ log.debug action }
    if (!state."desiredAssociation${group}") {
        state."desiredAssociation${group}" = nodes
    } else {
        switch (action) {
            case 0:
                state."desiredAssociation${group}" = state."desiredAssociation${group}" - nodes
            break
            case 1:
			    if( DebugOn ){ log.debug nodes }
                state."desiredAssociation${group}" = state."desiredAssociation${group}" + nodes
            break
        }
    }
}

def processAssociations(){
   def cmds = []
   setDefaultAssociations()
   def associationGroups = 5
   if (state.associationGroups) {
       associationGroups = state.associationGroups
   } else {
       if( DebugOn ){ log.debug "Getting supported association groups from device" }
       cmds <<  zwave.associationV2.associationGroupingsGet()
   }
   for (int i = 1; i <= associationGroups; i++){
      if(state."actualAssociation${i}" != null){
         if(state."desiredAssociation${i}" != null || state."defaultG${i}") {
            def refreshGroup = false
            ((state."desiredAssociation${i}"? state."desiredAssociation${i}" : [] + state."defaultG${i}") - state."actualAssociation${i}").each {
                if( DebugOn ){ log.debug "Adding node $it to group $i" }
                cmds << zwave.associationV2.associationSet(groupingIdentifier:i, nodeId:Integer.parseInt(it,16))
                refreshGroup = true
            }
            ((state."actualAssociation${i}" - state."defaultG${i}") - state."desiredAssociation${i}").each {
                if( DebugOn ){ log.debug "Removing node $it from group $i" }
                cmds << zwave.associationV2.associationRemove(groupingIdentifier:i, nodeId:Integer.parseInt(it,16))
                refreshGroup = true
            }
            if (refreshGroup == true) cmds << zwave.associationV2.associationGet(groupingIdentifier:i)
            else if( DebugOn ){ log.debug "There are no association actions to complete for group $i" }
         }
      } else {
         if( DebugOn ){ log.debug "Association info not known for group $i. Requesting info from device." }
         cmds << zwave.associationV2.associationGet(groupingIdentifier:i)
      }
   }
   return cmds
}

void zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
    def temp = []
    if (cmd.nodeId != []) {
       cmd.nodeId.each {
          temp += it.toString().format( '%02x', it.toInteger() ).toUpperCase()
       }
    } 
    state."actualAssociation${cmd.groupingIdentifier}" = temp
    if( DebugOn ){ log.debug "Associations for Group ${cmd.groupingIdentifier}: ${temp}" }
    updateDataValue("associationGroup${cmd.groupingIdentifier}", "$temp")
}

def zwaveEvent(hubitat.zwave.commands.associationv2.AssociationGroupingsReport cmd) {
    if( DebugOn ){ log.debug "Supported association groups: ${cmd.supportedGroupings}" }
    state.associationGroups = cmd.supportedGroupings
    createEvent(name: "groups", value: cmd.supportedGroupings)
}

void zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd) {
    if( DebugOn ){ log.debug cmd }
    if(cmd.applicationVersion && cmd.applicationSubVersion) {
	    def firmware = "${cmd.applicationVersion}.${cmd.applicationSubVersion.toString().padLeft(2,'0')}"
        state.needfwUpdate = "false"
        sendEvent(name: "status", value: "fw: ${firmware}")
        updateDataValue("firmware", firmware)
    }
}
2 Likes

WOW!
My sincere thanks for that - it's much appreciated.

Working on drivers (if the code is available) really is not that bad, so hopefully this helps.

@ericm: Something to include in upcoming drivers or maybe a respin of the current ones when you get a chance. I noticed other things about it that could probably use tweaking but I did not want to "mess with" it any more than the bare minimum for this case.

Thanks @snell, I really appreciate it. Most of our handlers were converted in the early days of Hubitat and they definitely could use some tweaking.

That aside, as mentioned, @jtmpush18 you might want to see what is causing all the "on()" commands to be sent. The device should be pretty quiet unless you try to control it with Hubitat or at the device itself.

@ericm: No worries. I figure you folks are busy getting the next generation ready.

1 Like

I am getting the same log entries as @jtmpush18. I never paid any attention before, but the last few days I was having some problems.

The plug has always worked great. Until about a week ago. Now every once in a while one or the other of the outlets just turns off. Last night for example about every 2 to 3 hours one of them shut off. I can turn it right back on ok. I have not changed anything as far as operation goes. Nothing in my system is doing it that I can tell.

So looking for some suggestions.

Follow up to my last post. One thing I did do was move the plug to a power strip. Still in same location, but needed to clean things up a bit.

Could the power strip cause a problem?
@ericm

Removed it from power strip. Didn't help . Still getting unexplained shutoffs.

That is really strange that it just started doing it. Do you have a different device connected to it or is it in a cabinet now or anything like that?

I had it plugged directly into a wall socket with 2 lamps plugged into it. I wanted to clean up the cord situation. So I put an outlet strip on a table bottom and plugged the unit into that. Still the same 2 lamps. I did have to unplug the lamps when I moved it and plugged them back in. It is about 18" from where it was originally. I removed it from the power strip after having issues and ran a heavy duty short cord to the wall outlet. Didn't make any difference.

It has worked fine for months. So I'm thinking maybe something got jarred and came loose inside it when I moved it. Don't know if that is possible or not.

When one or the other of the lights go off I can immediately signal them to come back on and they do. When I look at the events it shows them going off, but nothing in the logs or any reason for it. I can also push the button on the unit and turn the lights on/off no problem.

Usually it's only one or the other light that goes off and it seems to be about 3 hours between events. Last night one went off and about 10 seconds later the other one did. That was the first time both had went off in about the same time.

I'll keep playing with it to see if I can figure anything out. I'm going to move it back to the wall socket, but have to do some work on the cords from the lamps. Just haven't had time.

I took the unit and plugged it back directly into the wall socket. Have not had a problem since. Is it possible that having it on its side could have some affect?

Hi folks,

I have the Inovelli outdoor dual outlet.

NZW97

I can add the custom driver for this and also the child device.

I can get it to join my network.

I can set the DEVICE setting to be the one from the custom driver.

I can confirm I can control the whole thing to turn on and off.

How do I get to view and control the children?

Thanks!

If it didn't create the child devices, you may have to go into the driver and do a save preferences to get the child devices created.

image

image

The instructions IMHO, are still very unclear if ALL the additional apps on the github page are required, but the driver does show this:

image

I have everything installed, including the Virtual Device Sync app and mine work perfectly (except for the incessant logging.)

image

ROCK"N"ROLL!

Thanks!

That worked.

Hopefully these will be added natively. That Inovelli stuff is top-notch.

OK, how do I disable debug logging? It's killing my logs and slowing things down.

Simply putting # in front of the log.debug doesn't work.

@snell, do you have modified drivers for the NZW97 and NZW37 that can disable debug logging?

Try // instead of # (which I don't think is even a valid Groovy comment except as the "shebang" line, which does not apply to the Hubitat environment).