Orvibo Zigbee Relay RL804QZB

I bought one of these from BangGood (I’m in the UK). It looks like an interesting piece of kit. For less than £25 quid delivered it seems to be a lot cheaper than the Fibaro alternative.

It has 3 operational modes and can handle loads up to 5A.

Here’s where I need some help. It’s detected as a Thing and I have changed the device type in the IDE to ZigBee Switch and it’s working well.

My next challenge is to use it as a momentary switch in a Garage Door solution. Does anyone feel like helping me out with a modification of the SmartThings code to allow a momentary push???

I managed to get this working after having a look at the code and some other examples. I’ve used the relay to provide a momentary press to an existing roller door on a garage. The roller mechanism has all the required safety features built into it so I ended a simple momentary press to either lift or lower the door (I have a camera on it).

When I get 5 I’ll put a contact sensor on it so that I can close it after a period of activity.

To confirm this is an Orvibo Relay, using the native SmartThings Zigbee Switch device handler. To provide the momentary press the following code sorted it out:

/**
 *
 *  Modification of SmartThjings device handler to provide momentary press on a relay (Orvibo)
 *
 *  Copyright 2015 SmartThings
 *
 *  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: "ZigBee Switch (Relay)", namespace: "smartthings", author: "SmartThings") {
        capability "Actuator"
        capability "Configuration"
        capability "Refresh"
        capability "Switch"

        fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006"
    }

    // simulator metadata
    simulator {
        // status messages
        status "on": "on/off: 1"
        status "off": "on/off: 0"

        // reply messages
        reply "zcl on-off on": "on/off: 1"
        reply "zcl on-off off": "on/off: 0"
    }

    tiles(scale: 2) {
        multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
            tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
/*              attributeState "on", label:'${name}', action:"switch.off", icon:"st.Transportation.transportation14", backgroundColor:"#79b821", nextState:"turningOff"
                attributeState "off", label:'${name}', action:"switch.on", icon:"st.Transportation.transportation14", backgroundColor:"#ffffff", nextState:"turningOn"
                attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.Transportation.transportation14", backgroundColor:"#79b821", nextState:"turningOff"
                attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.Transportation.transportation14", backgroundColor:"#ffffff", nextState:"turningOn"
 */               
                attributeState "on", label:'${name}', action:"switch.off", icon:"st.Transportation.transportation14", backgroundColor:"#79b821", nextState:"off"
                attributeState "off", label:'${name}', action:"switch.on", icon:"st.Transportation.transportation14", backgroundColor:"#ffffff", nextState:"on"
 //               attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.Transportation.transportation14", backgroundColor:"#79b821", nextState:"turningOff"
 //               attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.Transportation.transportation14", backgroundColor:"#ffffff", nextState:"turningOn"
            }
        }
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
        }
        main "switch"
        details(["switch", "refresh"])
    }
}

// Parse incoming device messages to generate events
def parse(String description) {
    log.debug "description is $description"
    def event = zigbee.getEvent(description)
    if (event) {
        sendEvent(event)
    }
    else {
        log.warn "DID NOT PARSE MESSAGE for description : $description"
        log.debug zigbee.parseDescriptionAsMap(description)
    }
}

def off() {
    zigbee.off()
}

def on() {
	delayBetween([
	zigbee.on(),
	zigbee.off()
	], 10)
}

def refresh() {
    zigbee.onOffRefresh() + zigbee.onOffConfig()
}

def configure() {
    log.debug "Configuring Reporting and Bindings."
    zigbee.onOffConfig() + zigbee.onOffRefresh()
}
1 Like

does it works?

As a momentary switched relay? Yes. Not done any testing with anything else but i’m using it daily to automate my garage door.

Quick thought. The relay in question has three pairs of terminals. I could (if I try hard enough) find a use for another pair of terminals in the same location as the current installation.

Does anyone have any thoughts on how I might modify the standard ST DTH to control multiple pairs of terminals?

The relay itself has three modes: low power, high power and relay. If it was possible to control 3 items from a single relay at this price point that must be useful to quite a few?

Hello brumster
Been trying to make one work with a roller shutter but I really can’t figure out the wiring
Do you have any picture of the wiring or can you help me out how you wired yours?
It’s the first switch I’ve been trying to play with and no experience with electricity

Thanks

I simply used it as a dry contact relay to simulate a momentary button push.

This used to work with the modified DTH discussed above but ST changed something on the back end and it stopped working.

At the moment I have it working as follows.

I trigger the relay from the app. In the background I have a CoRE piston running that immediately turns the relay off as soon as it sees it turned on, thus simulating a momentary push.

From memory the relay has 3 endpoints (3 pairs of terminals). I’m only using 1 pair as I’m not a coder and I’m using the stock Smart Power Outlet handler.

If you pair the device, then use the IDE to assign the Smart Power Outlet device handler can you turn the relay off and on and do you hear it clicking?

I’ve just had a quick look, dip 3 is set (no power dry relay), and I have a pair of wires in terminal set 3 which go to the garage door.

Any help?

1 Like

Yes i hear it clicking, but i also have an orvibo hub, so it’s paired with it, for now

I’m Just trying the wiring part in a shutter roller before I move it to the smartthings

Tomorrow I’ll start over, thanks for your help

You got a multimeter?

No, i don’t :frowning:

I think I have to get one just to be able to connect this thing

Tomorrow I’ll give it another attempt, thanks

1 Like

Useful bit of kit. Less than a tenner on Amazon and worthwhile!

  • Unpair the relay from Orvibo
  • Make sure dip 3 is set
  • Restart the relay
  • Pair with ST
  • Once paired change the Device handler to a the stock SmartPowerOutlet using IDE
  • Set your multimeter to ohms, this will indicate wether there is resistance across contacts and therefore wether or not the connection is open or closed. Most modern meters will beep when a connection is present.
  • Place meter contacts across one of the pairs on the relay I’m using pair 3 (not sure why I’m not using pair 1)
  • Then turn the relay on and off using ST, the meter should tell you if all is working fine

I’m not sure what sort of garage door you have but if you need a momentary press configure CoRE as described above

If you need a hand shout, if I can help I will.

P

1 Like

If all 3 pairs could be made to work separately, this could be a cheap irrigation controller for me. Anyone have had any luck?

Great work on this relay, Paul. I got one and would like to use the three relays separately. How easy is it to modify the code? I have three blinds on my alfresco and I want to tap directly into each one’s remote control by just soldering a wire pair on button connections. The blinds are italian ASA and the motors are pretty much sealed. If successful, I can automatically close my blinds with afternoon sun or when away or at night.

Thanks

Willie

I got all three pairs working!

1 Like

Could you share your DTH?

Thanks.

Yep please share :slight_smile:

I am very rusty with my coding skills and borrowed code from all over. Firstly it was difficult to get documentation in english on the relay. Here is a copy:

Make sure switch 3 is on only. I attached the first and second relay to my blind’s remote control button:
IMG_4024

I have set up my device handler to act as a momentary switch. You can change it to treat each relay as a normal switch. Here is the device handler as normal switches:

/**
  *  Custom Device type for Orvibo Multi-purpose controller in relay mode
 
  */
 metadata {
 	definition (name: "Orvibo Multi-purpose controller", namespace: "WilliePrinsloo", author: "willie@prinsloo.com") {
         capability "Refresh"
         capability "Polling"
         capability "Configuration"
         capability "Switch"
         command "on1"
         command "off1"
         command "on2"
         command "off2"
         command "on3"
         command "off3"
         command "onoff1"
         command "onoff2"
         command "onoff3"
         attribute "switch1","ENUM", ["on","off"]
         attribute "switch2","ENUM", ["on","off"]
         attribute "switch3","ENUM", ["on","off"]


     	//fingerprint profileId: "0104", inClusters: "0000", outClusters: "000D,0006"
         fingerprint inClusters: "0000 0001 0003 0004 0005 0006", endpointId: "01", deviceId: "0100", profileId: "0104"

 	}

 	// simulator metadata
 	simulator {
     }

 	// UI tile definitions
 	tiles {
 		standardTile("switch1", "device.switch1", width: 1, height: 1, canChangeIcon: true) {
            state "off", label:'Down', action:"on1", icon:"st.Transportation.transportation14", backgroundColor:"#ffffff", nextState:"on"
            state "on", label:'Down', action:"off1", icon:"st.Transportation.transportation14", backgroundColor:"#79b821", nextState:"off"
        }
 		standardTile("switch2", "device.switch2", width: 1, height: 1, canChangeIcon: true) {
 			state "off", label: 'Up', action: "on2", icon: "st.Transportation.transportation14", backgroundColor: "#ffffff", nextState:"on"
 			state "on", label: 'Up', action: "off2", icon: "st.Transportation.transportation14", backgroundColor: "#79b821", nextState:"off"
 		}
        standardTile("switch3", "device.switch3", width: 1, height: 1, canChangeIcon: true) {
 			state "off", label: 'Stop', action: "on3", icon: "st.Transportation.transportation14", backgroundColor: "#ffffff", nextState:"on"
 			state "on", label: 'Stop', action: "off3", icon: "st.Transportation.transportation14", backgroundColor: "#79b821", nextState:"off"
 		}
         standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
 			state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
 		}

 		main (["switch1", "switch2", "switch3"])
 		details (["switch1", "switch2", "switch3", "refresh"])
 	}
 }

 // Parse incoming device messages to generate events
 def parse(String description) {
     log.debug "Parse description $description"
     def name = null
     def value = null

     if (description?.startsWith("catchall: 0104 0006 01")) {
         log.debug "On/Off command received from EP 1"
         if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001000")){
         	name = "switch1"
             value = "off"}
         else if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001001")){
         	name = "switch1"
             value = "on"}                        
     }  
     else if (description?.startsWith("catchall: 0104 0006 02")) {
         log.debug "On/Off command received from EP 2"    
         if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001000")){
         	name = "switch2"
             value = "off"}
         else if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001001")){
         	name = "switch2"
             value = "on"}
     }
     else if (description?.startsWith("catchall: 0104 0006 03")) {
         log.debug "On/Off command received from EP 3"    
         if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001000")){
         	name = "switch3"
             value = "off"}
         else if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001001")){
         	name = "switch3"
             value = "on"}
     }


 	def result = createEvent(name: name, value: value)
     log.debug "Parse returned ${result?.descriptionText}"
     return result
 }

 // Commands to device

 def on1() {
 	log.debug "Relay 1 on()"
 	sendEvent(name: "switch1", value: "on")
 	"st cmd 0x${device.deviceNetworkId} 0x01 0x0006 0x1 {}"
 }

 def off1() {
 	log.debug "Relay 1 off()"
 	sendEvent(name: "switch1", value: "off")
 	"st cmd 0x${device.deviceNetworkId} 0x01 0x0006 0x0 {}"
 }
 
 def onoff1() {
    log.debug "Relay 1 onoff()"
 	delayBetween([
    on1(),
    off1()
    ], 1000)
 }
 
 def on2() {
 	  log.debug "Relay 2 on()"
 	  sendEvent(name: "switch2", value: "on")
 	  "st cmd 0x${device.deviceNetworkId} 0x02 0x0006 0x1 {}"     
 }

 def off2() {
 	log.debug "Relay 2 off()"
 	sendEvent(name: "switch2", value: "off")
 	"st cmd 0x${device.deviceNetworkId} 0x02 0x0006 0x0 {}"
 }

def onoff2() {
    log.debug "Relay 1 onoff()"
 	delayBetween([
    on2(),
    off2()
    ], 1000)
 }
 
 def on3() {
     log.debug "Relay 3 on()"
 	 sendEvent(name: "switch3", value: "on")
 	 "st cmd 0x${device.deviceNetworkId} 0x03 0x0006 0x1 {}"
 }

 def off3() {
 	log.debug "Relay 3 off()"
 	sendEvent(name: "switch3", value: "off")
 	"st cmd 0x${device.deviceNetworkId} 0x03 0x0006 0x0 {}"
 }

 def onoff3() {
    log.debug "Relay 1 onoff()"
 	delayBetween([
    on3(),
    off3()
    ], 1000)
 }


 def poll(){
 	log.debug "Poll is calling refresh"
 	refresh()
 }

 def refresh() {
 	log.debug "sending refresh command"
     def cmd = []

     cmd << "st rattr 0x${device.deviceNetworkId} 0x01 0x0006 0x0000"	// Read on / off value at End point 0x01 
     cmd << "st wattr 0x${device.deviceNetworkId} 0x01 0x0006 0x0000"	// Read on / off value at End point 0x01 
     cmd << "delay 150"

     cmd << "st rattr 0x${device.deviceNetworkId} 0x02 0x0006 0x0000"	// Read on / off value at End point 0x02 
     cmd << "st wattr 0x${device.deviceNetworkId} 0x02 0x0006 0x0000"	// Read on / off value at End point 0x02 
     cmd << "delay 150"

     cmd << "st rattr 0x${device.deviceNetworkId} 0x03 0x0006 0x0000"	// Read on / off value at End point 0x03
     cmd << "st wattr 0x${device.deviceNetworkId} 0x03 0x0006 0x0000"	// Read on / off value at End point 0x03
     cmd
     zigbee.onOffRefresh() + zigbee.onOffConfig()
 }



 def configure() {
 	log.debug "Binding SEP 0x01 and 0x02 and 0x03 DEP 0x01 Cluster 0x0006 On / Off cluster to hub" 
     def cmd = []
     cmd << "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0006 {${device.zigbeeId}} {}"	    // Bind on/off output to SmartThings hub for end point 1
     cmd << "delay 150"
     cmd << "zdo bind 0x${device.deviceNetworkId} 0x02 0x01 0x0006 {${device.zigbeeId}} {}" 	// Bind on/off output to SmartThings hub for end point 2
     cmd << "delay 150"
     cmd << "zdo bind 0x${device.deviceNetworkId} 0x03 0x01 0x0006 {${device.zigbeeId}} {}" 	// Bind on/off output to SmartThings hub for end point 3
     cmd
 }

Here is the device handler as momentary switches:

/**
  *  Custom Device type for Orvibo Multi-purpose controller in relay mode
 
  */
 metadata {
 	definition (name: "Orvibo Multi-purpose controller", namespace: "WilliePrinsloo", author: "willie@prinsloo.com") {
         capability "Refresh"
         capability "Polling"
         capability "Configuration"
         capability "Switch"
         command "on1"
         command "off1"
         command "on2"
         command "off2"
         command "on3"
         command "off3"
         command "onoff1"
         command "onoff2"
         command "onoff3"
         attribute "switch1","ENUM", ["on","off"]
         attribute "switch2","ENUM", ["on","off"]
         attribute "switch3","ENUM", ["on","off"]


     	//fingerprint profileId: "0104", inClusters: "0000", outClusters: "000D,0006"
         fingerprint inClusters: "0000 0001 0003 0004 0005 0006", endpointId: "01", deviceId: "0100", profileId: "0104"

 	}

 	// simulator metadata
 	simulator {
     }

 	// UI tile definitions
 	tiles {
 		standardTile("switch1", "device.switch1", width: 1, height: 1, canChangeIcon: true) {
            state "off", label:'Down', action:"onoff1", icon:"st.Transportation.transportation14", backgroundColor:"#ffffff", nextState:"on"
            state "on", label:'Down', action:"off1", icon:"st.Transportation.transportation14", backgroundColor:"#79b821", nextState:"off"
        }
 		standardTile("switch2", "device.switch2", width: 1, height: 1, canChangeIcon: true) {
 			state "off", label: 'Up', action: "onoff2", icon: "st.Transportation.transportation14", backgroundColor: "#ffffff", nextState:"on"
 			state "on", label: 'Up', action: "off2", icon: "st.Transportation.transportation14", backgroundColor: "#79b821", nextState:"off"
 		}
        standardTile("switch3", "device.switch3", width: 1, height: 1, canChangeIcon: true) {
 			state "off", label: 'Stop', action: "onoff3", icon: "st.Transportation.transportation14", backgroundColor: "#ffffff", nextState:"on"
 			state "on", label: 'Stop', action: "off3", icon: "st.Transportation.transportation14", backgroundColor: "#79b821", nextState:"off"
 		}
         standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
 			state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
 		}

 		main (["switch1", "switch2", "switch3"])
 		details (["switch1", "switch2", "switch3", "refresh"])
 	}
 }

 // Parse incoming device messages to generate events
 def parse(String description) {
     log.debug "Parse description $description"
     def name = null
     def value = null

     if (description?.startsWith("catchall: 0104 0006 01")) {
         log.debug "On/Off command received from EP 1"
         if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001000")){
         	name = "switch1"
             value = "off"}
         else if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001001")){
         	name = "switch1"
             value = "on"}                        
     }  
     else if (description?.startsWith("catchall: 0104 0006 02")) {
         log.debug "On/Off command received from EP 2"    
         if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001000")){
         	name = "switch2"
             value = "off"}
         else if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001001")){
         	name = "switch2"
             value = "on"}
     }
     else if (description?.startsWith("catchall: 0104 0006 03")) {
         log.debug "On/Off command received from EP 3"    
         if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001000")){
         	name = "switch3"
             value = "off"}
         else if (description?.endsWith(" 01 0140 00 38A8 00 00 0000 01 01 0000001001")){
         	name = "switch3"
             value = "on"}
     }


 	def result = createEvent(name: name, value: value)
     log.debug "Parse returned ${result?.descriptionText}"
     return result
 }

 // Commands to device

 def on1() {
 	log.debug "Relay 1 on()"
 	sendEvent(name: "switch1", value: "on")
 	"st cmd 0x${device.deviceNetworkId} 0x01 0x0006 0x1 {}"
 }

 def off1() {
 	log.debug "Relay 1 off()"
 	sendEvent(name: "switch1", value: "off")
 	"st cmd 0x${device.deviceNetworkId} 0x01 0x0006 0x0 {}"
 }
 
 def onoff1() {
    log.debug "Relay 1 onoff()"
 	delayBetween([
    on1(),
    off1()
    ], 1000)
 }
 
 def on2() {
 	  log.debug "Relay 2 on()"
 	  sendEvent(name: "switch2", value: "on")
 	  "st cmd 0x${device.deviceNetworkId} 0x02 0x0006 0x1 {}"     
 }

 def off2() {
 	log.debug "Relay 2 off()"
 	sendEvent(name: "switch2", value: "off")
 	"st cmd 0x${device.deviceNetworkId} 0x02 0x0006 0x0 {}"
 }

def onoff2() {
    log.debug "Relay 1 onoff()"
 	delayBetween([
    on2(),
    off2()
    ], 1000)
 }
 
 def on3() {
     log.debug "Relay 3 on()"
 	 sendEvent(name: "switch3", value: "on")
 	 "st cmd 0x${device.deviceNetworkId} 0x03 0x0006 0x1 {}"
 }

 def off3() {
 	log.debug "Relay 3 off()"
 	sendEvent(name: "switch3", value: "off")
 	"st cmd 0x${device.deviceNetworkId} 0x03 0x0006 0x0 {}"
 }

 def onoff3() {
    log.debug "Relay 1 onoff()"
 	delayBetween([
    on3(),
    off3()
    ], 1000)
 }


 def poll(){
 	log.debug "Poll is calling refresh"
 	refresh()
 }

 def refresh() {
 	log.debug "sending refresh command"
     def cmd = []

     cmd << "st rattr 0x${device.deviceNetworkId} 0x01 0x0006 0x0000"	// Read on / off value at End point 0x01 
     cmd << "st wattr 0x${device.deviceNetworkId} 0x01 0x0006 0x0000"	// Read on / off value at End point 0x01 
     cmd << "delay 150"

     cmd << "st rattr 0x${device.deviceNetworkId} 0x02 0x0006 0x0000"	// Read on / off value at End point 0x02 
     cmd << "st wattr 0x${device.deviceNetworkId} 0x02 0x0006 0x0000"	// Read on / off value at End point 0x02 
     cmd << "delay 150"

     cmd << "st rattr 0x${device.deviceNetworkId} 0x03 0x0006 0x0000"	// Read on / off value at End point 0x03
     cmd << "st wattr 0x${device.deviceNetworkId} 0x03 0x0006 0x0000"	// Read on / off value at End point 0x03
     cmd
     zigbee.onOffRefresh() + zigbee.onOffConfig()
 }



 def configure() {
 	log.debug "Binding SEP 0x01 and 0x02 and 0x03 DEP 0x01 Cluster 0x0006 On / Off cluster to hub" 
     def cmd = []
     cmd << "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0006 {${device.zigbeeId}} {}"	    // Bind on/off output to SmartThings hub for end point 1
     cmd << "delay 150"
     cmd << "zdo bind 0x${device.deviceNetworkId} 0x02 0x01 0x0006 {${device.zigbeeId}} {}" 	// Bind on/off output to SmartThings hub for end point 2
     cmd << "delay 150"
     cmd << "zdo bind 0x${device.deviceNetworkId} 0x03 0x01 0x0006 {${device.zigbeeId}} {}" 	// Bind on/off output to SmartThings hub for end point 3
     cmd
 }   

You then need to create three virtual switches to be able to use each of the relay switches as separate “things”. Then a smart app is required to keep them in sync.

I am still struggling to make the virtual switches also act as momentary switches. Currently I just set up smart lighting apps to switch each on and off after a minute.

2 Likes

Here is my virtual device handler based on standard simulated switch:

/**
 *  Copyright 2015 SmartThings
 *
 *  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: "Orvibo Simulated Switch", namespace: "WilliePrinsloo", author: "willie@prinsloo.com") {
        capability "Switch"
        capability "Relay Switch"
        capability "Actuator"

    }

    tiles {
        standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
            state "off", label: '${currentValue}', action: "switch:on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"on"
            state "on", label: '${currentValue}', action: "switch:off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
        }
        main "switch"
        details(["switch","on","off"])
    }
}

def parse(description) {
}

def on() {
    log.debug "$version on()"
    sendEvent(name: "switch", value: "on")

}



def off() {
    log.debug "$version off()"
    sendEvent(name: "switch", value: "off")
}

private getVersion() {
    "PUBLISHED"
}

and here is the smart app added to the physical relay device to sync or bind the virtual and physical switch states:

/**
 *  Virtual / Physical Switch Sync (i.e. Enerwave ZWN-RSM2 Adapter, Monoprice Dual Relay, Philio PAN04, Aeon SmartStrip)
 *
 *  Copyright 2016 Eric Maycock (erocm123)
 * 
 *  Note: Use a "Simulated Switch" from the IDE for best results
 *
 *  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: "Virtual / Physical Switch Sync",
   namespace: "erocm123",
   author: "Eric Maycock",
   description: "Keeps multi switch devices like the Aeon Smartstrip, Monoprice Dual Relay, and Philio PAN04 in sync with their virtual switches",
   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 {
    page(name: "numberPage", nextPage: "setupPage")
    page(name: "setupPage")
}

def numberPage() {
    dynamicPage(name: "numberPage", install: false, uninstall: true) {
        section {
            input "vNumber", "number", title:"Number of virtual switches", description: 2, defaultValue: 2 , required: true
        }
        section([title:"Available Options", mobileOnly:true]) {
			label title:"Assign a name for your app (optional)", required:false
		}
    }

}

def setupPage() {
    dynamicPage(name: "setupPage", install: true, uninstall: true) {
    section {
        input "physical", "capability.switch", title: "Which Physical Switch?", multiple: false, required: true
        for (int i = 1; i <= vNumber; i++){
            input "virtual${i}", "capability.switch", title: "Virtual Switch to link to Switch ${i}?", multiple: false, required: true        
        }
    }
    }
}

def installed() {
  log.debug "Installed with settings: ${settings}"
  initialize()
}

def updated() {
  log.debug "Updated with settings: ${settings}"
  unsubscribe()
  initialize()
}

def initialize() {
  log.debug "Initializing Virtual / Physical Switch Sync v 1.0"
  for (int i = 1; i <= vNumber; i++){
     subscribe(physical, "switch${i}", physicalHandler)
     subscribeToCommand(settings["virtual${i}"], "on", virtualHandler)
     subscribeToCommand(settings["virtual${i}"], "off", virtualHandler)
    
  }
}

def virtualHandler(evt) {
  log.debug "virtualHandler called with event: deviceId ${evt.deviceId} name:${evt.name} source:${evt.source} value:${evt.value} isStateChange: ${evt.isStateChange()} isPhysical: ${evt.isPhysical()} isDigital: ${evt.isDigital()} data: ${evt.data} device: ${evt.device}"
    for (int i = 1; i <= vNumber; i++){
       if (evt.deviceId == settings["virtual${i}"].id) {
             physical."${evt.value}${i}"()
       }
    }
}

def physicalHandler(evt) {
  log.debug "physicalHandler called with event:  name:${evt.name} source:${evt.source} value:${evt.value} isStateChange: ${evt.isStateChange()} isPhysical: ${evt.isPhysical()} isDigital: ${evt.isDigital()} data: ${evt.data} device: ${evt.device}"
  for (int i = 1; i <= vNumber; i++){
       if (evt.name == "switch${i}") {
            try {
                sendEvent(settings["virtual${i}"], [name:"switch", value:"$evt.value", type:"physical"])
			} catch (e) {
                log.trace "Caught error: Likely caused by not using my specialized Simulated Switches"
				log.trace e
                settings["virtual${i}"]."${evt.value}Physical"()		
			}
       }
    }
}

These devices were cheaper and more available but seems to be a bit more difficult to find now. Amazon does not have it and the only place where I could get them was at Fasttech: ORVIBO ZigBee Smart Home Multi-functional Remote Control Relay

Please let me know if this works for you and if you can make the virtual device also act as momentary switch.

Hey Willie,

Thanks for writing the DH and Smart Apps for these Relays.
I have just started with SmartThings and got two of these relays for the garage door and swamp cooler (turn on and off).

Both your relay device handlers (momentary and standard) are working, I can hear the click when using the device handler with the device.

However I created three virtual switches using your virtual switch handler and then used the smart app you created to tie them to the relay device, however when I toggle the virtual switches, nothing happens on the relay (I don’t hear the relay clicking). Any ideas?

Also, in regards to the momentary relay device handler, the relay displays the DOWN status, and when I press it the relay clicks on and off which is expected, however, I want the app to display UP or ON instead of DOWN. Is there a way to modify the handler to do this?

In the standard relay device handler, it shows the switch as DOWN and then I press it, it simply highlights DOWN, but doesn’t change the text to UP.

Thanks in advance.

Remember to add the smart app to the physical item and to configure it so that it can sync. Perhaps rather use the virtual DH by erocm123 which you can find here: https://github.com/erocm123/SmartThingsPublic/blob/master/devicetypes/erocm123/simulated-switch.src/simulated-switch.groovy

I just received two more relays to control all three of my blinds and will spend a bit more time on this. The main DH works and the momentary switch version works but I want to make the virtual DH also momentary. Perhaps I should ask erocm123 for some help.