[Deprecated] Inovelli 2-Channel Outdoor Smart Plug NZW97 Driver (ST Port)

UPDATED 04/13/18:
Inovelli has released their official drivers (see link below). I will leave my port up for historical purposes and advise everyone to use the official version going forward.


Another ST Port.
Thanks to @ericmaycock for coding this for ST and allowing me to share. This is a dual Channel device.
Auto Off and LED options have been tested and work.

On/Off applies to both outlets.
Custom Commands on1() and off1() apply to Outlet 1
Custom Commands on2() and off2() apply to Outlet 2

/**
 *  Inovelli 2-Channel Outdoor Smart Plug NZW97
 *  Author: Eric Maycock (erocm123)
 *  Date: 2017-11-14
 *
 *  Copyright 2017 Eric Maycock
 *
 *	Edited for Hubitat by Stephan Hackett 3/26/18
 *
 *
 *  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: "Inovelli 2-Channel Outdoor Smart Plug NZW97", namespace: "erocm123", author: "Eric Maycock") {
        capability "Actuator"
        capability "Sensor"
        capability "Switch"
        capability "Polling"
        capability "Refresh"
        //capability "Health Check"
        capability "PushableButton"
        
        attribute "lastActivity", "String"
        attribute "lastEvent", "String"
        attribute "switch1", "String"
        attribute "switch2", "String"
        
        command "on1"
        command "on2"
        command "off1"
        command "off2"

        fingerprint manufacturer: "015D", prod: "6100", model: "6100", deviceJoinName: "Inovelli 2-Channel Outdoor Smart Plug"
        fingerprint manufacturer: "0312", prod: "6100", model: "6100", deviceJoinName: "Inovelli 2-Channel Outdoor Smart Plug"
        fingerprint manufacturer: "015D", prod: "0221", model: "611C", deviceJoinName: "Inovelli 2-Channel Outdoor Smart Plug"
        fingerprint manufacturer: "0312", prod: "0221", model: "611C", deviceJoinName: "Inovelli 2-Channel Outdoor Smart Plug"
    }
    
    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
    }
    
}
def parse(String description) {
    def result = []
    def cmd = zwave.parse(description)
    if (cmd) {
        result += zwaveEvent(cmd)
        log.debug "Parsed ${cmd} to ${result.inspect()}"
    } else {
        log.debug "Non-parsed event: ${description}"
    }
    return result
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd, ep = null) {
    log.debug "BasicReport ${cmd} - ep ${ep}"
    if (ep) {
        def event
        if(ep != 0) sendEvent(name: "switch${ep}", value: cmd.value ? "on" : "off")
        
        if (cmd.value) {
            event = [createEvent([name: "switch", value: "on"])]
        } else {
            def allOff = true
            if (device.currentState("switch1").value != "off") allOff = false
            if (device.currentState("switch2").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) {
    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()
}

def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) {
    log.debug "SwitchBinaryReport ${cmd} - ep ${ep}"
    if (ep) {
        def event
        if(ep != 0) sendEvent(name: "switch${ep}", value: cmd.value ? "on" : "off")
        if (cmd.value) {
            event = [createEvent([name: "switch", value: "on"])]
        } 
        else {
            def allOff = true
            if (device.currentState("switch1").value != "off") allOff = false
            if (device.currentState("switch2").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) {
    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) {
    log.debug "ManufacturerSpecificReport ${cmd}"
    def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
    log.debug "msr: $msr"
    updateDataValue("MSR", msr)
}

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
    log.debug "Unhandled Event: ${cmd}"
}

def on() {
    log.debug "on()"
    commands([
            zwave.switchAllV1.switchAllOn(),
            encap(zwave.switchBinaryV1.switchBinaryGet(), 1),
            encap(zwave.switchBinaryV1.switchBinaryGet(), 2)
    ])
}

def off() {
    log.debug "off()"
    commands([
            zwave.switchAllV1.switchAllOff(),
            encap(zwave.switchBinaryV1.switchBinaryGet(), 1),
            encap(zwave.switchBinaryV1.switchBinaryGet(), 2)
    ])
}

def on1(){
    log.debug "Switch 1 On"
    zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format()
    //def cmds = []
    //cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0xFF), 1)))
    //cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 1)))
    //sendHubCommand(cmds, 1000)
}

def on2(){
    log.debug "Switch 2 On"
    zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[255]).format()
    //def cmds = []
    //cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0xFF), 2)))
    //cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 2)))
    //sendHubCommand(cmds, 1000)
}

def off1() {
    log.debug "Switch 1 Off"
    zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format()
    //def cmds = []
    //cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0x00), 1)))
    //cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 1)))
    //sendHubCommand(cmds, 1000)
}

def off2() {
    log.debug "Switch 2 Off"
    zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[0]).format()
    //def cmds = []
    //cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0x00), 2)))
    //cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), 2)))
    //sendHubCommand(cmds, 1000)
}

def poll() {
    log.debug "poll()"
    commands([
            encap(zwave.switchBinaryV1.switchBinaryGet(), 1),
            encap(zwave.switchBinaryV1.switchBinaryGet(), 2),
    ])
}

def refresh() {
    log.debug "refresh()"
    commands([
            encap(zwave.switchBinaryV1.switchBinaryGet(), 1),
            encap(zwave.switchBinaryV1.switchBinaryGet(), 2),
    ])
}

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

def installed() {
    log.debug "installed()"
    command(zwave.manufacturerSpecificV1.manufacturerSpecificGet())
    setSwitchDefaults()
}

def updated() {
    log.debug "updated()"
    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 = []
    cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId).format()
    cmds += zwave.associationV2.associationGet(groupingIdentifier:1).format()
    cmds += zwave.configurationV1.configurationSet(configurationValue: [ledIndicator? ledIndicator.toInteger() : 0], parameterNumber: 1, size: 1).format()
    cmds += zwave.configurationV1.configurationGet(parameterNumber: 1).format()
    cmds += zwave.configurationV1.configurationSet(scaledConfigurationValue: autoOff1? autoOff1.toInteger() : 0, parameterNumber: 2, size: 2).format()
    cmds += zwave.configurationV1.configurationGet(parameterNumber: 2).format()
    cmds += zwave.configurationV1.configurationSet(scaledConfigurationValue: autoOff2? autoOff2.toInteger() : 0, parameterNumber: 3, size: 2).format()
    cmds += zwave.configurationV1.configurationGet(parameterNumber: 3).format()
    if (cmds) return delayBetween(cmds, 500)
    //response(commands(cmds))
}

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

def setSwitchDefaults() {
    sendEvent(name: "switch1", value: "off", displayed: true)
    sendEvent(name: "switch2", value: "off", displayed: true)
}

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)
}
4 Likes

This driver also works great with Inovellie Dual Plug w/Built-In Repeater (non-dimming) HZW37

Inovelli now has an app to create custom endpoints for each plug too. Works great! Thanks again to both Eric’s at Inovelli for the great support.

I expect there will be an official release of the Inovelli apps and drivers soon. I believe the items listed on their github are a work in progress and may not be ready for release yet. I will let @ericm chime in that.

@bptworld They are a work in progress, but most of them should work.

I tested the “Virtual Device Sync” app after several changes to it (from my SmartThings version) and it seems to be working.

I’m on vacation right now, but when I get back I plan on releasing handlers for the rest of our products so stay tuned! :wink:

Anyone have the 1 channel outdoor version working?
Inovelli 1-Channel Outdoor Smart Plug NZW96

I tried to bring in the smartthings handler but after making a few easy changes still no luck.

dev:1042018-04-07 11:27:04.169:warnNo signature of method: java.util.LinkedHashMap.putAll() is applicable for argument types: (java.util.LinkedHashMap$Entry) values: [0=On] Possible solutions: putAll(java.util.Map), putAll(java.util.Map), putAll(java.util.Collection), putAt(java.lang.Object, java.lang.Object), putAt(java.lang.String, java.lang.Object), findAll()

dev:1042018-04-07 11:25:05.068:errorNo signature of method: dev15231177968481955519927.logsOff() is applicable for argument types: () values: [] Possible solutions: off(), notify(), off1(), off2() on line null

You can try this:

I haven’t tested it yet, but it will likely work. It uses pretty standard Z-Wave command classes so I’d be surprised if it gave you any issues.

I gave it a try this morning. Same issues as what I had experienced with the one i attempted to modify.

[dev:104] 2018-04-08 10:06:26.051:warnNo signature of method: java.util.LinkedHashMap.putAll() is applicable for argument types: (java.util.LinkedHashMap$Entry) values: [0=On] Possible solutions: putAll(java.util.Map), putAll(java.util.Map), putAll(java.util.Collection), putAt(java.lang.Object, java.lang.Object), putAt(java.lang.String, java.lang.Object), findAll()

[dev:104] 2018-04-08 10:06:25.095:warnNo signature of method: java.util.LinkedHashMap.putAll() is applicable for argument types: (java.util.LinkedHashMap$Entry) values: [0=On] Possible solutions: putAll(java.util.Map), putAll(java.util.Map), putAll(java.util.Collection), putAt(java.lang.Object, java.lang.Object), putAt(java.lang.String, java.lang.Object), findAll()

Take a look at the DTH in the first post. The options map (ledIndicator input) needs to be formatted the same.
i.e. you need the extra hard brackets [ ]

1 Like

Thanks @stephack, I applied the fix you suggested and have tested it. Seems to be working. One thing is that I am seeing is that “updated()” isn’t being called when a configuration change is made? Do I need to add the Configuration capability and create a “configure()” method to make preference changes go through?

I just tested one of my drivers, and updated() was called successfully after I pressed the SAVE button directly beneath the user preferences.

Note: There is a second SAVE button, used when changing the device’s name, type etc… I’m not sure if pressing that save button will call updated(). I think not.

Oh yeah, so the updated method in SmartThings has you return commands as a response, but Hubitat doesn’t? Changing the following made it work:

response(commands(cmds))

to

commands(cmds)

1 Like

I am a working well with the drivers from @ericm

1 Like

Copied from a related thread:

It took me a while to sort out how to get the switch to work as separate switches, so heres the following guide:

Go to the github page linked above and copy the raw text for your device
paste this into the drivers code--> new driver on hubitat.

Add the zwave device as normal, following Inovelli's included instructions (standard discover +power on device near hubitat)

Set the zwave device type as the inovelli device and save. You should now be able to manually control he device, but not automate it independently. To do this, add the following:

Add the "simulated switch" config to your drivers code --New driver

Now, copy the "Virtual apps sync" config from inovelli's github into apps code--> new app
Go to Apps, click new app, then load the new user app "Virtual device sync" at the bottom.

From here, select the actual switch, then select the type of virtual switch to make, and you should now have two accessible switches you can automate rules with.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.