Virtual Switches and Dual Relays

Guys, not sure you guys know but I am using the dual relay ST smartapps and able to tie 2 virtual switches to the dual relay just like ST. Here's the link to the post from ST with the app in there.

This doesn’t work at all for me. The relay device updates instantly when I trigger either physical switch, great!

But neither assigned virtual switches do anything. And more worrisome is the relay device page doesn’t register on/off consistently, polling or refreshing sometimes updates the status but even that is flaky.

What did you do to get it working?

I am using this driver Device Polling - #15 by bravenel

and apps


/**
 *  Dual Relay Adapter (i.e. Enerwave ZWN-RSM2 Adapter, Monoprice Dual Relay, Philio PAN04)
 *
 *  Copyright 2014 Joel Tamkin
 *
 *	2015-10-29: erocm123 - I removed the scheduled refreshes for my Philio PAN04 as it supports instant
 *	status updates with my custom device type
 *
 *  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.
 *
 */
definition(
    name: "joel Dual Relay Adapter",
    namespace: "",
    author: "Joel Tamkin",
    description: "Associates Dual Relay Switch Modules with one or two standard SmartThings 'switch' devices for compatibility with standard control and automation techniques",
    category: "My Apps",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")


preferences {
  section("ZWN-RSM2 Module:") {
    input "rsm", "capability.switch", title: "Which RSM2 Module?", multiple: false, required: true
    input "switch1", "capability.switch", title: "First Switch?", multiple: false, required: true
    input "switch2", "capability.switch", title: "Second Switch?", multiple: false, required: false
  }
}

def installed() {
  log.debug "Installed!"
  //subscribe(rsm, "switch", rsmHandler)
  subscribe(rsm, "switch1", rsmHandler)
  subscribe(rsm, "switch2", rsmHandler)
  subscribe(switch1, "switch", switchHandler)
  subscribe(switch2, "switch", switchHandler)

  initialize()
}

def updated() {
  log.debug "Updated!"
  unsubscribe()
  subscribe(rsm, "switch", rsmHandler)
  subscribe(rsm, "switch1", rsmHandler)
  subscribe(rsm, "switch2", rsmHandler)
  subscribe(switch1, "switch", switchHandler)
  subscribe(switch2, "switch", switchHandler)
  
  initialize()
}

def switchHandler(evt) {
  //log.debug "switchHandler: ${evt.value}, ${evt.deviceId}, ${evt.source}, ${switch2.id}"
  switch (evt.deviceId) {
  	case switch1.id:
		switch (evt.value) {
        	case 'on':
        		log.debug "switch 1 on"
                rsm.on1()
                break
        	case 'off':
        		log.debug "switch 1 off"
                rsm.off1()
                break
            }
        break
    case switch2.id:
    	switch (evt.value) {
        	case 'on':
        		log.debug "switch 2 on"
                rsm.on2()
                break
        	case 'off':
        		log.debug "switch 2 off"
                rsm.off2()
                break
            }
        break

    default:
    	pass
  }
}

def rsmHandler(evt) {
	log.debug "$evt.name $evt.value"
    if (evt.name == "switch1") {
    	switch (evt.value) {
        	case 'on':
            	switch1.on()
                break
            case 'off':
            	switch1.off()
                break
        }
    }
    else if (evt.name == "switch2") {
    	switch (evt.value) {
        	case 'on':
            	switch2.on()
                break
            case 'off':
            	switch2.off()
                break
        }
    }
      	
}

def rsmRefresh() {
	rsm.refresh()
}
    
def initialize() {
    unschedule()
    //def exp = "* * * * * ?"
    //log.debug "Scheduling RSM refreshes"
	//schedule(exp, rsmRefresh)
    // TODO: subscribe to attributes, devices, locations, etc.
}

So I finally had time to come back to this as I've migrated more dual relay's over. I get no status update on the dual relay device itself. And as I noticed before, setting a trigger in RM won't work because it only seems the "main" switch.

I've double checked I'm using the right code, and also went back to Joel's app vs Eric's, neither work though.

Are you really getting updates on the physical switch on/off of either relay? Poll and Refresh on the device don't do anything, well Poll gives me a null thing...

Sorry for the late reply. I don't really use the physical switches for these dual relays. I created 2 virtual switches and use Joel app to tie them to the dual relay.

Has anyone modified this dual relay driver to utilize the new child device functions? I would love to eliminate the app that ties a virtual switch to the 2nd relay.

So I have modified some code I found around the forums that uses parent/child device drivers to handle all my Monoprice dual relays in case it helps others. No more need for a smart app or virtual switches and once the children are created they can be renamed to anything you like.

/*
 *  Monoprice/Vision Dual Relay Parent Driver
 */
metadata {
    definition (name: "Dual Relay Driver", namespace: "hubitat", author: "hubitat") {
        capability "Refresh"
        capability "Actuator"
        
        command "childOn"
        command "childOff"
        command "recreateChildDevices"
        command "deleteChildren"

        //fingerprint manufacturer: "015D", prod: "0651", model: "F51C", deviceJoinName: "Zooz ZEN20 Power Strip"
        //fingerprint deviceId: "0x1004", inClusters: "0x5E,0x85,0x59,0x5A,0x72,0x60,0x8E,0x73,0x27,0x25,0x86"
   }
}

def installed() {
    log.debug "installed"
    createChildDevices()
    configure()
}

def updated() {
    log.debug "updated"
    
    if (!childDevices) {
        createChildDevices()
    }
    else if (device.label != state.oldLabel) {
        childDevices.each {
            def newLabel = "$device.displayName (CH${channelNumber(it.deviceNetworkId)})"
            it.setLabel(newLabel)
        }
  
        state.oldLabel = device.label
    }

    configure()
}

def configure() {
    log.debug "configure"
    def cmds = [
                   zwave.versionV1.versionGet().format(),
                   zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
                   zwave.firmwareUpdateMdV2.firmwareMdGet().format()
               ]
    response(delayBetween(cmds, 1000))
}

def refresh() {
    log.debug "refresh"
    def cmds = []
    cmds << zwave.basicV1.basicGet().format()
    cmds << zwave.switchBinaryV1.switchBinaryGet().format()
    
    (1..2).each { endpoint ->
        cmds << encap(zwave.basicV1.basicGet(), endpoint)
        cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint)
    }
 
    delayBetween(cmds, 100)
}

def recreateChildDevices() {
    log.debug "recreateChildDevices"
    deleteChildren()
    createChildDevices()
}

def deleteChildren() {
	log.debug "deleteChildren"
	def children = getChildDevices()
    
    children.each {child->
  		deleteChildDevice(child.deviceNetworkId)
    }
}


def parse(String description) {
    log.debug "parse $description"
    def result = null
 
    if (description.startsWith("Err")) {
        result = createEvent(descriptionText:description, isStateChange:true)
    } else {
        def cmd = zwave.parse(description, [0x60: 3, 0x25: 1, 0x20: 1])
        log.debug "Command: ${cmd}"
  
        if (cmd) {
            result = zwaveEvent(cmd)
            log.debug "parsed '${description}' to ${result.inspect()}"
        } else {
            log.debug "Unparsed description $description"
        }
    }
 
    result
}


def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
    log.debug "multichannelv3.MultiChannelCmdEncap $cmd"
    def encapsulatedCommand = cmd.encapsulatedCommand([0x25: 1, 0x20: 1])
    log.debug "encapsulatedCommand: $encapsulatedCommand"
 
    if (encapsulatedCommand) {
        return zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer)
    } else {
        log.debug "Unable to get encapsulated command: $encapsulatedCommand"
        return []
    }
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd, endpoint = null) {
    log.debug "basicv1.BasicReport $cmd, $endpoint"
    zwaveBinaryEvent(cmd, endpoint, "digital")
}

def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, endpoint = null) {
    log.debug "switchbinaryv1.SwitchBinaryReport $cmd, $endpoint"
    zwaveBinaryEvent(cmd, endpoint, "physical")
}

def zwaveBinaryEvent(cmd, endpoint, type) {
    log.debug "zwaveBinaryEvent cmd $cmd, endpoint $endpoint, type $type"
    def childDevice = childDevices.find{it.deviceNetworkId.endsWith("$endpoint")}
    def result = null
 
    if (childDevice) {
        log.debug "childDevice.sendEvent $cmd.value"
        childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off", type: type)
    } else {
        result = createEvent(name: "switch", value: cmd.value ? "on" : "off", type: type)
    }
 
    result
}

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

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

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

def zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd, endpoint) {
   log.debug "versionv1.VersionReport, applicationVersion $cmd.applicationVersion, cmd $cmd, endpoint $endpoint"
}

def zwaveEvent(hubitat.zwave.Command cmd, endpoint) {
    log.debug "${device.displayName}: Unhandled ${cmd}" + (endpoint ? " from endpoint $endpoint" : "")
}

def zwaveEvent(hubitat.zwave.Command cmd) {
    log.debug "${device.displayName}: Unhandled ${cmd}"
// My Code
  runIn( 1, refresh )
///My Code
}

def on() {
    log.debug "on"
    def cmds = []
    cmds << zwave.basicV1.basicSet(value: 0xFF).format()
    cmds << zwave.basicV1.basicGet().format()
    
    (1..2).each { endpoint ->
        cmds << encap(zwave.basicV1.basicSet(value: 0xFF), endpoint)
        cmds << encap(zwave.basicV1.basicGet(), endpoint)
    }

    return delayBetween(cmds, 1000)
}

def off() {
    log.debug "off"
    def cmds = []
    cmds << zwave.basicV1.basicSet(value: 0x00).format()
    cmds << zwave.basicV1.basicGet().format()
    
    (1..2).each { endpoint ->
        cmds << encap(zwave.basicV1.basicSet(value: 0x00), endpoint)
        cmds << encap(zwave.basicV1.basicGet(), endpoint)
    }
    
    return delayBetween(cmds, 1000)
}

def childOn(String dni) {
    onOffCmd(0xFF, channelNumber(dni))
}

def childOff(String dni) {
    onOffCmd(0, channelNumber(dni))
}

private onOffCmd(value, endpoint) {
    log.debug "onOffCmd, value: $value, endpoint: $endpoint"
    
    def cmds = []
    cmds << encap(zwave.basicV1.basicSet(value: value), endpoint)
    cmds << encap(zwave.basicV1.basicGet(), endpoint)
    
    return delayBetween(cmds, 1000)
}

private channelNumber(String dni) {
    def ch = dni.split("-")[-1] as Integer
    return ch
}

private void createChildDevices() {
    log.debug "createChildDevices"
    
    for (i in 1..2) {
        addChildDevice("hubitat", "Dual Relay Driver (Child)", "$device.deviceNetworkId-$i", [name: "ch$i", label: "$device.displayName $i", isComponent: true])
    }
}

private encap(cmd, endpoint) {
    if (endpoint) {
        zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd).format()
    } else {
        cmd.format()
    }
}
/*
 *  Monoprice/Vision Dual Relay Child Device Driver
 */
metadata {
    definition (name: "Dual Relay Driver (Child)", namespace: "hubitat", author: "hubitat") {
        capability "Switch"
        capability "Actuator"
    }
}

void on() { 
    log.debug "$device on"
    parent.childOn(device.deviceNetworkId)
}

void off() {
    log.debug "$device off"
    parent.childOff(device.deviceNetworkId)
}

Once you change the device driver click the "Recreate Children" and it should make your two devices one for each relay.

Here is what it looks like when your done and they show up as individual switches in RM and other apps.

12 Likes

I tried this on my Enerwave Dual Relay's, it doesn't respond. The ST Monoprice and Enerwave were basically interchangeable. Any idea what would be different with your modified one?

I also tried to rejoin my Monoprice ones, but HE won't finish finding them, I've had a nightmare of a time with some z-wave devices joining for some reason. They join right back up to ST no problem, go figure.

Edit: Magically the one Enerwave started working! It's quicker than the old driver too. I think it's showing active status more accurately as well from physical switches. But it's still a big lagged. Switch one always worked better even for ST and that behavior is similar here. Looks like I don't need my RM rule to refresh anymore at least!

Thanks for doing this!

Glad its working for you. I have always had trouble adding/removing these dual relays. I have found the best way is to remove the gang box switch cover and press the button on the relay 4 times quickly. That seems to work most of the time or at least after a few tries.

I don't have the Monoprice but I find the only way for me with the Vision dual relay is to reset it first by holding the button on the relay and power it up.
I think the vision is very similar to the Monoprice relay.

1 Like

After playing with this more, I see I'm not getting updates and refresh doesn't do anything. Clicking child in or child off in the parent generates an error:

dev:4472018-08-28 00:16:50.846:debugrefresh

dev:4472018-08-28 00:16:43.082:errorjava.lang.NullPointerException: Cannot invoke method split() on null object on line 218 (childOn)

dev:4472018-08-28 00:16:23.973:errorjava.lang.NullPointerException: Cannot invoke method split() on null object on line 218 (childOff)

dev:4472018-08-28 00:15:51.979:debugrefresh

dev:4472018-08-28 00:15:35.693:debugonOffCmd, value: 255, endpoint: 1

Any thoughts on this? Physical switch events seem to register.

That is strange. You should be using the Child on/off buttons at all. These are in the parent device only for use by the child devices. They don't do anything when pressed in the UI. I use only the on/off buttons on the child devices themselves and everything is working fine for me. I wish I could be more help but I’m not much of a programmer.

Did you hit the recreate child devices when you first installed the driver on the device? It should create two devices, one for each relay, that will work independently just like any other switch device.

I did the driver change, the recreate button, all worked except updates, then tried randomly hitting on/off stuff to see if it mattered! :slight_smile:

This is with my Enerwave relay, I still can't get HE to pair my three monoprice relay's. Once I resolve that, we'll see if it's Enerwave specific.

I'm really hating HE's Z-Wave discovery right now. I have at least 5 devices it won't discovery, after forced exclusion, when ST will. First world problems, I know.

Hey @michaelahess, I totally see your frustration with the Monoprice dual relay. I have 3 as well and for me. I have to reset the relay first and not Z-Wave exclusion. You can do this by holding the little button while power up the relay. One of the relay I have to reboot the hub to get it discovered.

Oh yeah, been through that a bunch, reboots, reset button, all of it. I'll play with it more this weekend, see if it'll magically start working. Thanks!

1 Like

I am using this driver for my Fibaro dual relays, and they are working as advertised.

Anyone try this with the Shelly relay switch?

2 Likes

Isn't Shelly switch is WiFi? This is a Z-Wave driver/apps. Low chance it will work.

Ditto.

BTW, the Parent/Child DH above worked perfect for me. Thanks!

Thanks for the work! Maybe the issue with connecting this device is this caveat in the Installation Manual for pairing this beast that initial setup requires less than 3 ft from hub? image