Fibaro FGS-221 and FGS-223


#1

Has anyone found or built a driver that works for Fibaro FGS-221 and FGS-223? I have them running in smartthings but I am migrating to hubitat. Or can I use the code from my ST device handler and use it in Hubitat?


#2

Pretty sure this built in driver works for them. fibaro1


#3

I have one for both. Will post when I get the chance.


#4

Will this give the chance to control the 2 childs switches independently?


#5

Yes.


#6

Wonderful. Wait to hear. Tks


#7

Here is the driver for the FGS-222.

/**
 *  
 *	Fibaro FGS-222 Double Relay Switch Device Type - For use on Hubitat
 *  
 *	Author: Robin Winbourne
 *	Date: 2018-03-24 
 */
 
metadata {
definition (name: "Fibaro FGS-222 Double Relay Switch", namespace: "hubitat", author: "Robin Winbourne") {
capability "Switch"
capability "Relay Switch"
capability "Polling"
capability "Configuration"
capability "Refresh"
//capability "Zw Multichannel"

attribute "switch1", "string"
attribute "switch2", "string"

command "on1"
command "off1"
command "on2"
command "off2"
    
command "updateSingleParam" // This custom command can be used with Rule Machine or webCoRE, to send parameter values (paramNr & paramvalue) to the device

fingerprint deviceId: "0x1001", inClusters:"0x86, 0x72, 0x85, 0x60, 0x8E, 0x25, 0x20, 0x70, 0x27"

}
    
   preferences {
        input name: "param1", type: "enum", defaultValue: "255", required: true,
            title: "Parameter No. 1 - Activate / deactivate functions ALL ON / ALL OFF. Default value: 255.",
       		options: [
                    ["255" : "255 - ALL ON active, ALL OFF active"],
                    ["0" : "0 - ALL ON is not active ALL OFF is not active"],
                    ["1" : "1 - ALL ON is not active ALL OFF active"],
                    ["2" : "2 - ALL ON active ALL OFF is not active"]
                ]
       
        input name: "param3", type: "enum", defaultValue: "0", required: true,
            title: "Parameter No. 3 - Auto off relay after specified time, with the possibility of manual override - immediate Off after button push. Default value: 0.",
       		options: [
                    ["0" : "0 - Manual override disabled"],
                    ["1" : "1 - Manual override enabled"]
                ]

		input name: "param4", type: "number", range: "0..65535", defaultValue: "0", required: true,
            title: "Parameter No. 4 - Auto off for relay 1.  " +
                   "Available settings:\n" +
                   "[1 - 65535] (0,1 s – 6553,5 s) Time period for auto off, in miliseconds,\n" +
                   "0 - Auto off disabled.\n" +
                   "Default value: 0."

		input name: "param5", type: "number", range: "0..65535", defaultValue: "0", required: true,
            title: "Parameter No. 5 - Auto off for relay 2.  " +
                   "Available settings:\n" +
                   "[1 - 65535] (0,1 s – 6553,5 s) Time period for auto off, in miliseconds,\n" +
                   "0 - Auto off disabled.\n" +
                   "Default value: 0."

		input name: "param6", type: "enum", defaultValue: "0", required: true,
            title: "Parameter No. 6 - Sending commands to control devices assigned to 1st association group (key no. 1). " +
                   "NOTE: Parameter 15 value must be set to 1 to work properly. Default value: 0.",
       		options: [
                    ["0" : "0 - Commands are sent when device is turned on and off"],
                    ["1" : "1 - Commands are sent when device is turned off"],
                	["2" : "2 - Commands are sent when device is turned off"]
                ]

       		input name: "param7", type: "enum", defaultValue: "0", required: true,
            title: "Parameter No. 7 - Sending commands to control devices assigned to 2nd association group (key no. 2). " +
                   "NOTE: Parameter 15 value must be set to 1 to work properly. Default value: 0.",
       		options: [
                    ["0" : "0 - Commands are sent when device is turned on and off"],
                    ["1" : "1 - Commands are sent when device is turned off"],
                	["2" : "2 - Commands are sent when device is turned off"]
                ]

		input name: "param13", type: "enum", defaultValue: "0", required: true,
            title: "Parameter No. 13 - Assigns bistable key status to the device. Default value: 0.",
       		options: [
                    ["0" : "0 - Device changes status on key status change"],
                    ["1" : "1 - Device status depends on key status: ON when the key is ON"]
                ]

		input name: "param14", type: "enum", defaultValue: "1", required: true,
            title: "Parameter No. 14 - Switch type connector, you may choose between momentary and toggle switches. Default value: 1.",
       		options: [
                    ["0" : "0 - Momentary switch"],
                    ["1" : "1 - Toggle switch"]
                ]

		input name: "param15", type: "enum", defaultValue: "0", required: true,
            title: "Parameter No. 15 - Operation of the Dimmer and Roller Shutter Controller - enabling this option allows the user to dim lighting/shut roller by associating Dimmer/Roller Shutter Controller and holding or double press of double switch (only mono-stable switch). Default value: 0.",
       		options: [
                    ["0" : "0 - Dimmer/Roller Shutter Controller control is not active"],
                    ["1" : "1 - Dimmer/Roller Shutter Controller control is active"]
                ]

		input name: "param16", type: "enum", defaultValue: "1", required: true,
            title: "Parameter No. 16 - Saving the state of the device after a power failure. Default value: 1.",
       		options: [
                    ["0" : "0 - Switch returns to 'off' position"],
                    ["1" : "1 - Switch saves its state before power failure"]
                ]

        input name: "param30", type: "enum", defaultValue: "3", required: true,
            title: "Parameter No. 30 - Relay 1 - Response to General Alarm. Default value: 3.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]

     	input name: "param31", type: "enum", defaultValue: "2", required: true,
            title: "Parameter No. 31 - Relay 1 - Response to Flood Alarm. Default value: 2.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]
               
       input name: "param32", type: "enum", defaultValue: "3", required: true,
            title: "Parameter No. 32 - Relay 1 - Response to Smoke, CO, CO2 Alarm. Default value: 3.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]
       
       input name: "param33", type: "enum", defaultValue: "1", required: true,
            title: "Parameter No. 33 - Relay 1 - Response to Temperature Alarm. Default value: 1.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]

    	input name: "param40", type: "enum", defaultValue: "3", required: true,
            title: "Parameter No. 40 - Relay 2 - Response to General Alarm. Default value: 3.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]

     	input name: "param41", type: "enum", defaultValue: "2", required: true,
            title: "Parameter No. 41 - Relay 2 - Response to Flood Alarm. Default value: 2.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]
               
       input name: "param42", type: "enum", defaultValue: "3", required: true,
            title: "Parameter No. 42 - Relay 2 - Response to Smoke, CO, CO2 Alarm. Default value: 3.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]
       
       input name: "param43", type: "enum", defaultValue: "1", required: true,
            title: "Parameter No. 43 - Relay 2 - Response to Temperature Alarm. Default value: 1.",
       		options: [
                    ["0" : "0 - Switch does not respond to alarm"],
                    ["1" : "1 - Switch turns on after detecting an alarm"],
                	["2" : "2 - Switch turns off after detecting an alarm"],
                	["3" : "3 - Switch flashes after detecting an alarm"]
                ]

		input name: "param39", type: "number", range: "0..65535", defaultValue: "600", required: true,
            title: "Parameter No. 39 - Active flashing alarm time. " +
            	   "This parameter allows to set time parameter used in timed modes.\n" +
                   "Available settings:\n" +
                   "[1-65535][ms].\n" +
                   "Default value: 600."
       
    input name: "paramAssociationGroup1", type: "bool", defaultValue: true, required: true,
             title: "The Fibaro Sigle Switch provides the association of three groups.\n\n" +
                    "1st group is assigned to key no. 1.\n" +
                    "Default value: true"

        input name: "paramAssociationGroup2", type: "bool", defaultValue: true, required: true,
             title: "2nd group is assigned to key no. 2.\n" +
                    "Default value: true"

        input name: "paramAssociationGroup3", type: "bool", defaultValue: false, required: true,
             title: "3rd group reports state of devices. Only one device can be associated to this group.\n" +
                    "Default value: false"
    }
}

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.BasicSet cmd) {
	sendEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital")
    def result = []
    result << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
    result << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
    response(delayBetween(result, 1000)) // returns the result of reponse()
}

def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd)
{
    sendEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital")
    def result = []
    result << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
    result << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
    response(delayBetween(result, 1000)) // returns the result of reponse()
}


def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) 
{
    log.debug "multichannelv3.MultiChannelCapabilityReport $cmd"
    if (cmd.endPoint == 2 ) {
        def currstate = device.currentState("switch2").getValue()
        if (currstate == "on")
        	sendEvent(name: "switch2", value: "off", isStateChange: true, display: false)
        else if (currstate == "off")
        	sendEvent(name: "switch2", value: "on", isStateChange: true, display: false)
    }
    else if (cmd.endPoint == 1 ) {
        def currstate = device.currentState("switch1").getValue()
        if (currstate == "on")
        sendEvent(name: "switch1", value: "off", isStateChange: true, display: false)
        else if (currstate == "off")
        sendEvent(name: "switch1", value: "on", isStateChange: true, display: false)
    }
}

def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
   def map = [ name: "switch$cmd.sourceEndPoint" ]
    
   switch(cmd.commandClass) {
      case 32:
         if (cmd.parameter == [0]) {
            map.value = "off"
         }
         if (cmd.parameter == [255]) {
            map.value = "on"
         }
         createEvent(map)
         break
      case 37:
         if (cmd.parameter == [0]) {
            map.value = "off"
         }
         if (cmd.parameter == [255]) {
            map.value = "on"
         }
         break
    }
    def events = [createEvent(map)]
    if (map.value == "on") {
            events += [createEvent([name: "switch", value: "on"])]
    } else {
         def allOff = true
         (1..2).each { n ->
             if (n != cmd.sourceEndPoint) {
                 if (device.currentState("switch${n}").value != "off") allOff = false
             }
         }
         if (allOff) {
             events += [createEvent([name: "switch", value: "off"])]
         }
    }
    events
}

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
    return createEvent(descriptionText: "${device.displayName}: ${cmd}")
}

def zwaveEvent(hubitat.zwave.commands.switchallv1.SwitchAllReport cmd) {
   log.debug "SwitchAllReport $cmd"
}

def refresh() {
	def cmds = []
    cmds << zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()
	cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
    cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
	delayBetween(cmds, 1000)
}

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

def poll() {
	def cmds = []
	cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
    cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
	delayBetween(cmds, 1000)
}

def configure() {
	log.debug "Executing 'configure'"
    delayBetween([
          zwave.configurationV1.configurationSet(parameterNumber:1, configurationValue:[param1.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:3, configurationValue:[param3.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:4, configurationValue:[param4.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:5, configurationValue:[param5.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:6, configurationValue:[param6.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:7, configurationValue:[param7.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:13, configurationValue:[param13.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:14, configurationValue:[param14.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:15, configurationValue:[param15.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:16, configurationValue:[param16.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:30, configurationValue:[param30.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:31, configurationValue:[param31.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:32, configurationValue:[param32.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:33, configurationValue:[param33.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:39, configurationValue:[param39.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:40, configurationValue:[param40.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:41, configurationValue:[param41.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:42, configurationValue:[param42.value]).format(),
          zwave.configurationV1.configurationSet(parameterNumber:43, configurationValue:[param43.value]).format(),
          zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
          zwave.associationV2.associationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format(),
          zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format(),
    ])  
}

def updateSingleparam(paramNum, paramValue) {
	log.debug "Updating single Parameter (paramNum: $paramNum, paramValue: $paramValue)"
    	zwave.configurationV1.configurationSet(parameterNumber: paramNum, ConfigurationValue: paramValue)
}

/**
* Triggered when Save button is pushed on Preference UI
*/
def updated()
{
	log.debug "Preferences have been changed. Attempting configure()"
    def cmds = configure()
    response(cmds)
}
def on() { 
   delayBetween([
        zwave.switchAllV1.switchAllOn().format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
    ], 500)
}
def off() {
   delayBetween([
        zwave.switchAllV1.switchAllOff().format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
    ], 500)
}

def on1() {
    delayBetween([
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
    ], 500)
}

def off1() {
    delayBetween([
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
    ], 500)
}

def on2() {
    delayBetween([
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[255]).format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format()
    ], 500)
}

def off2() {
    delayBetween([
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:1, parameter:[0]).format(),
        zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format()
    ], 500)
}

Use this driver with the for the 2 switches for the 222.

/**
 *  Copyright 2018 Eric Maycock
 *
 *  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.
 *
 *  2018-03-27: Adapted for Hubitat
 */
metadata {
	
definition (name: "Simulated Switch", namespace: "erocm123", author: "Eric Maycock") {
		capability "Switch"
    capability "Actuator"

		command "onPhysical"
		command "offPhysical"
	}

	tiles(scale: 2) {
		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.light.off", backgroundColor: "#ffffff", nextState:"turningOn"
            attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#00a0dc", nextState:"turningOff"
            attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
            attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00a0dc", nextState:"turningOff"	
			}
		}
    main "switch"
		details(["switch"])
	}
}

def parse(String description) {

}

def parse(Map description) {
def eventMap
if (description.type == null) eventMap = [name:"$description.name", value:"$description.value"]
else eventMap = [name:"$description.name", value:"$description.value", type:"$description.type"]
createEvent(eventMap)
}

def extSendEvent(Map description) {
	def eventMap
if (description.type == null) eventMap = [name:"$description.name", value:"$description.value"]
else eventMap = [name:"$description.name", value:"$description.value", type:"$description.type"]
sendEvent(eventMap)
}

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

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

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

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

private getVersion() {
	"PUBLISHED"
}

#8

Heres the driver for the 223.

/**
 *  Note: This handler requires the "Metering Switch Child Device" to be installed.
 *
 *  Copyright 2016 Eric Maycock
 *
 *  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.
 *
 *  Fibaro FGS-223 Dual Relay
 *
 *  Author: Eric Maycock (erocm123)
 *  
 *  04/25/2017 - Fix for combined energy & power reports & switch endpoints showing correct info.
 *  04/18/2017 - This handler requires the Metering Switch Child device to create the multiple switch endpoints.
 */
 
metadata {
definition (name: "Fibaro Double Switch 2 FGS-223", namespace: "erocm123", author: "Eric Maycock") {
    capability "Sensor"
    capability "Actuator"
    capability "Switch"
    capability "Polling"
    capability "Configuration"
    capability "Refresh"
    //capability "Zw Multichannel"
    capability "Energy Meter"
    capability "Power Meter"
    capability "Health Check"
    capability "PushableButton"
    capability "HoldableButton"

    command "reset"
    command "childOn"
        command "childOff"
        command "childRefresh"
	command "childReset"


    fingerprint mfr: "010F", prod: "0203", model: "2000", deviceJoinName: "Fibaro Double Switch 2"
    fingerprint mfr: "010F", prod: "0203", model: "1000", deviceJoinName: "Fibaro Double Switch 2"

    fingerprint deviceId: "0x1001", inClusters:"0x5E,0x86,0x72,0x59,0x73,0x22,0x56,0x32,0x71,0x98,0x7A,0x25,0x5A,0x85,0x70,0x8E,0x60,0x75,0x5B"
}

simulator {
}

tiles(scale: 2){

    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"
			}
            tileAttribute ("statusText", key: "SECONDARY_CONTROL") {
           		attributeState "statusText", label:'${currentValue}'       		
            }
	}
	valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) {
			state "default", label:'${currentValue} W'
	}
    valueTile("energy", "device.energy", decoration: "flat", width: 2, height: 2) {
			state "default", label:'${currentValue} kWh'
	}
    standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
    }
    standardTile("configure", "device.needUpdate", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "NO" , label:'', action:"configuration.configure", icon:"st.secondary.configure"
            state "YES", label:'', action:"configuration.configure", icon:"https://github.com/erocm123/SmartThingsPublic/raw/master/devicetypes/erocm123/qubino-flush-1d-relay.src/configure@2x.png"
    }
    standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		state "default", label:'reset kWh', action:"reset"
	}
    
    main(["switch"])
    details(["switch", 
             "refresh","reset","configure"])

}
    preferences {
        input description: "Once you change values on this page, the corner of the \"configuration\" icon will change orange until all configuration parameters are updated.", title: "Settings", displayDuringSetup: false, type: "paragraph", element: "paragraph"
		generate_preferences(configuration_model())
    }
}

def parse(String description) {
	//log.debug 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}"
    }
    
    def statusTextmsg = ""
    
    result.each {
        if ((it instanceof Map) == true && it.find{ it.key == "name" }?.value == "power") {
            statusTextmsg = "${it.value} W ${device.currentValue('energy')? device.currentValue('energy') : "0"} kWh"
        }
        if ((it instanceof Map) == true && it.find{ it.key == "name" }?.value == "energy") {
            statusTextmsg = "${device.currentValue('power')? device.currentValue('power') : "0"} W ${it.value} kWh"
        }
    }
    if (statusTextmsg != "") sendEvent(name:"statusText", value:statusTextmsg, displayed:false)

    return result
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd)
{
    log.debug "BasicReport $cmd"
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicSet cmd, ep=null) {
    logging("BasicSet: $cmd : Endpoint: $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.switchbinaryv1.SwitchBinaryReport cmd, ep=null)
{
    logging("SwitchBinaryReport: $cmd : Endpoint: $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
    } else {
        def cmds = []
        cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1)
        cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2)
        return response(commands(cmds)) // returns the result of reponse()
    }
}

def zwaveEvent(hubitat.zwave.commands.meterv3.MeterReport cmd, ep=null) {
    logging("MeterReport: $cmd : Endpoint: $ep")
    def result
    def cmds = []
    if (cmd.scale == 0) {
       result = [name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]
    } else if (cmd.scale == 1) {
       result = [name: "energy", value: cmd.scaledMeterValue, unit: "kVAh"]
    } else {
       result = [name: "power", value: cmd.scaledMeterValue, unit: "W"]
    }
    if (ep) {
       def childDevice = childDevices.find{it.deviceNetworkId == "$device.deviceNetworkId-ep$ep"}
       if (childDevice)
          childDevice.sendEvent(result)
       def combinedValue = 0.00
       childDevices.each {
           if(it.currentValue(result.name)) combinedValue += it.currentValue(result.name)
       }
       return createEvent([name: result.name, value: combinedValue])
    } else {
       (1..2).each { endpoint ->
			cmds << encap(zwave.meterV2.meterGet(scale: 0), endpoint)
            cmds << encap(zwave.meterV2.meterGet(scale: 2), endpoint)
	   }
       return response(commands(cmds))
    }
}

def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) 
{
    //log.debug "multichannelv3.MultiChannelCapabilityReport $cmd"
    if (cmd.endPoint == 2 ) {
        def currstate = device.currentState("switch2").getValue()
        if (currstate == "on")
        	sendEvent(name: "switch2", value: "off", isStateChange: true, display: false)
        else if (currstate == "off")
        	sendEvent(name: "switch2", value: "on", isStateChange: true, display: false)
    }
    else if (cmd.endPoint == 1 ) {
        def currstate = device.currentState("switch1").getValue()
        if (currstate == "on")
        sendEvent(name: "switch1", value: "off", isStateChange: true, display: false)
        else if (currstate == "off")
        sendEvent(name: "switch1", value: "on", isStateChange: true, display: false)
    }
}

def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
   //logging("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.associationv2.AssociationReport cmd) {
	log.debug "AssociationReport $cmd"
    if (zwaveHubNodeId in cmd.nodeId) state."association${cmd.groupingIdentifier}" = true
    else state."association${cmd.groupingIdentifier}" = false
}

def zwaveEvent(hubitat.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) {
	log.debug "MultiChannelAssociationReport $cmd"
    if (cmd.groupingIdentifier == 1) {
        if ([0,zwaveHubNodeId,1] == cmd.nodeId) state."associationMC${cmd.groupingIdentifier}" = true
        else state."associationMC${cmd.groupingIdentifier}" = false
    }
}

def zwaveEvent(hubitat.zwave.Command cmd) {
    log.debug "Unhandled event $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
    return createEvent(descriptionText: "${device.displayName}: ${cmd}")
}

def zwaveEvent(hubitat.zwave.commands.switchallv1.SwitchAllReport cmd) {
   log.debug "SwitchAllReport $cmd"
}

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

def refresh() {
	def cmds = []
    (1..2).each { endpoint ->
        cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint)
		cmds << encap(zwave.meterV2.meterGet(scale: 0), endpoint)
        cmds << encap(zwave.meterV2.meterGet(scale: 2), endpoint)
	}
	commands(cmds, 1000)
}

def reset() {
    logging("reset()")
	def cmds = []
    (1..2).each { endpoint ->
        cmds << encap(zwave.meterV2.meterReset(), endpoint)
        cmds << encap(zwave.meterV2.meterGet(scale: 0), endpoint)
        cmds << encap(zwave.meterV2.meterGet(scale: 2), endpoint)
    }
	commands(cmds, 1000)
}

def ping() {
	def cmds = []
    cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1)
    cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2)
	commands(cmds, 1000)
}

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

def poll() {
	def cmds = []
    cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1)
    cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2)
	commands(cmds, 1000)
}

def configure() {
	state.enableDebugging = settings.enableDebugging
    logging("Configuring Device For SmartThings Use")
    def cmds = []

    cmds = update_needed_settings()
    
    if (cmds != []) commands(cmds)
}

def zwaveEvent(hubitat.zwave.commands.centralscenev1.CentralSceneNotification cmd) {
    logging("CentralSceneNotification: $cmd")
    logging("sceneNumber: $cmd.sceneNumber")
    logging("sequenceNumber: $cmd.sequenceNumber")
    logging("keyAttributes: $cmd.keyAttributes")
    
    buttonEvent(cmd.keyAttributes + 1, (cmd.sceneNumber == 1? "pushed" : "held"))

}

def buttonEvent(button, value) {
    logging("buttonEvent() Button:$button, Value:$value")
	sendEvent(name: value, value: button, isStateChange:true)
}

def installed() {
    def cmds = initialize()
	if (cmds != []) commands(cmds)
}

def updated()
{
    logging("updated() is being called")
    def cmds = initialize()
    sendEvent(name:"needUpdate", value: device.currentValue("needUpdate"), displayed:false, isStateChange: true)  
    if (cmds != []) commands(cmds)
}

def initialize() {
    log.debug "initialize()"
	state.enableDebugging = settings.enableDebugging
    if (!childDevices) {
		createChildDevices()
	}
	else if (device.label != state.oldLabel) {
		childDevices.each {
            if (it.label == "${state.oldLabel} (S${channelNumber(it.deviceNetworkId)})") {
			    def newLabel = "${device.displayName} (S${channelNumber(it.deviceNetworkId)})"
			    it.setLabel(newLabel)
            }
		}
		state.oldLabel = device.label
	}
    sendEvent(name: "checkInterval", value: 2 * 30 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
    return update_needed_settings()
}


def on() { 
   commands([
        encap(zwave.basicV1.basicSet(value: 0xFF), 1),
        encap(zwave.basicV1.basicSet(value: 0xFF), 2)
    ])
}
def off() {
   commands([
        encap(zwave.basicV1.basicSet(value: 0x00), 1),
        encap(zwave.basicV1.basicSet(value: 0x00), 2)
    ])
}

def childOn(String dni) {
    logging("childOn($dni)")
    def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0xFF), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
	cmds
}

def childOff(String dni) {
    logging("childOff($dni)")
	def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0x00), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
	cmds
}

def childRefresh(String dni) {
    logging("childRefresh($dni)")
	def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds << new hubitat.device.HubAction(command(encap(zwave.meterV2.meterGet(scale: 0), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds << new hubitat.device.HubAction(command(encap(zwave.meterV2.meterGet(scale: 2), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
	cmds
}

def childReset(String dni) {
    logging("childReset($dni)")
	def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.meterV2.meterReset(), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds << new hubitat.device.HubAction(command(encap(zwave.meterV2.meterGet(scale: 0), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds << new hubitat.device.HubAction(command(encap(zwave.meterV2.meterGet(scale: 2), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
	cmds
}

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 encap(cmd, endpoint) {
	if (endpoint) {
		zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd)
	} else {
		cmd
	}
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
	//log.debug cmd
	def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x32: 3, 0x25: 1, 0x98: 1, 0x70: 2, 0x85: 2, 0x9B: 1, 0x90: 1, 0x73: 1, 0x30: 1, 0x28: 1, 0x2B: 1, , 0x5B: 1]) // can specify command class versions here like in zwave.parse
	if (encapsulatedCommand) {
	    state.sec = 1
		return zwaveEvent(encapsulatedCommand)
	} else {
		log.warn "Unable to extract encapsulated cmd from $cmd"
		createEvent(descriptionText: cmd.toString())
	}
}

def generate_preferences(configuration_model)
{
    def configuration = new XmlSlurper().parseText(configuration_model)
   
    configuration.Value.each
    {
        switch(it.@type)
        {   
            case ["byte","short","four"]:
                input "${it.@index}", "number",
                    title:"${it.@label}\n" + "${it.Help}",
                    range: "${it.@min}..${it.@max}",
                    defaultValue: "${it.@value}"
            break
            case "list":
                def items = []
                it.Item.each { items << ["${it.@value}":"${it.@label}"] }
                input "${it.@index}", "enum",
                    title:"${it.@label}\n" + "${it.Help}",
                    defaultValue: "${it.@value}",
                    options: items
            break
            case "decimal":
               input "${it.@index}", "decimal",
                    title:"${it.@label}\n" + "${it.Help}",
                    range: "${it.@min}..${it.@max}",
                    defaultValue: "${it.@value}"
            break
            case "boolean":
               input "${it.@index}", "bool",
                    title: it.@label != "" ? "${it.@label}\n" + "${it.Help}" : "" + "${it.Help}",
                    defaultValue: "${it.@value}"
            break
            case "paragraph":
               input title: "${it.@label}",
                    description: "${it.Help}",
                    type: "paragraph",
                    element: "paragraph"
            break
        }  
    }
}

def update_current_properties(cmd)
{
    def currentProperties = state.currentProperties ?: [:]
    
    currentProperties."${cmd.parameterNumber}" = cmd.configurationValue

    if (settings."${cmd.parameterNumber}" != null)
    {
        if (convertParam(cmd.parameterNumber, settings."${cmd.parameterNumber}") == cmd2Integer(cmd.configurationValue))
        {
            sendEvent(name:"needUpdate", value:"NO", displayed:false, isStateChange: true)
        }
        else
        {
            sendEvent(name:"needUpdate", value:"YES", displayed:false, isStateChange: true)
        }
    }

    state.currentProperties = currentProperties
}

def update_needed_settings()
{
    def cmds = []
    def currentProperties = state.currentProperties ?: [:]
     
    def configuration = new XmlSlurper().parseText(configuration_model())
    def isUpdateNeeded = "NO"
    
    sendEvent(name:"numberOfButtons", value:"5")
    
    if(!state.associationMC1) {
       logging("Adding MultiChannel association group 1")
       cmds << zwave.associationV2.associationRemove(groupingIdentifier: 1, nodeId: [])
	   //cmds << zwave.associationV2.associationGet(groupingIdentifier: 1)
       cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 1, nodeId: [0,zwaveHubNodeId,1])
       cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1)
    }
    if(state.association2){
       logging("Removing association group 2")
       cmds << zwave.associationV2.associationRemove(groupingIdentifier:2, nodeId:zwaveHubNodeId)
       cmds << zwave.associationV2.associationGet(groupingIdentifier:2)
    }
    if(state.association4){
       logging("Removing association group 4")
       cmds << zwave.associationV2.associationRemove(groupingIdentifier:4, nodeId:zwaveHubNodeId)
       cmds << zwave.associationV2.associationGet(groupingIdentifier:4)
    }
    
    configuration.Value.each
    {     
        if ("${it.@setting_type}" == "zwave"){
            if (currentProperties."${it.@index}" == null)
            {
                isUpdateNeeded = "YES"
                logging("Current value of parameter ${it.@index} is unknown")
                cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
            }
            else if (settings."${it.@index}" != null && cmd2Integer(currentProperties."${it.@index}") != convertParam(it.@index.toInteger(), settings."${it.@index}"))
            { 
                isUpdateNeeded = "YES"

                logging("Parameter ${it.@index} will be updated to " + convertParam(it.@index.toInteger(), settings."${it.@index}"))
                def convertedConfigurationValue = convertParam(it.@index.toInteger(), settings."${it.@index}")
                cmds << zwave.configurationV1.configurationSet(configurationValue: integer2Cmd(convertedConfigurationValue, it.@byteSize.toInteger()), parameterNumber: it.@index.toInteger(), size: it.@byteSize.toInteger())
                cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
            } 
        }
    }
    
    sendEvent(name:"needUpdate", value: isUpdateNeeded, displayed:false, isStateChange: true)
    return cmds
}

def convertParam(number, value) {
    def parValue
	switch (number){
    	case 28:
            parValue = (value == true ? 1 : 0)
            parValue += (settings."fc_2" == true ? 2 : 0)
            parValue += (settings."fc_3" == true ? 4 : 0)
            parValue += (settings."fc_4" == true ? 8 : 0)
		    log.debug parValue
        break
        case 29:
            parValue = (value == true ? 1 : 0)
            parValue += (settings."sc_2" == true ? 2 : 0)
            parValue += (settings."sc_3" == true ? 4 : 0)
            parValue += (settings."sc_4" == true ? 8 : 0)
        break
        default:
        	parValue = value
        break
    }
    return parValue.toInteger()
}

private def logging(message) {
    if (state.enableDebugging == null || state.enableDebugging == true) log.debug "$message"
}

/**
* Convert 1 and 2 bytes values to integer
*/
def cmd2Integer(array) { 

switch(array.size()) {
	case 1:
		array[0]
    break
	case 2:
    	((array[0] & 0xFF) << 8) | (array[1] & 0xFF)
    break
    case 3:
    	((array[0] & 0xFF) << 16) | ((array[1] & 0xFF) << 8) | (array[2] & 0xFF)
    break
	case 4:
    	((array[0] & 0xFF) << 24) | ((array[1] & 0xFF) << 16) | ((array[2] & 0xFF) << 8) | (array[3] & 0xFF)
	break
    }
}

def integer2Cmd(value, size) {
	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
	}
}

def zwaveEvent(hubitat.zwave.commands.crc16encapv1.Crc16Encap cmd) {
   def encapsulatedCommand = zwave.getCommand(cmd.commandClass, cmd.command, cmd.data,1)
   if (encapsulatedCommand) {
       zwaveEvent(encapsulatedCommand)
   } else {
       log.warn "Unable to extract CRC16 command from ${cmd}"
   }
}

private channelNumber(String dni) {
	dni.split("-ep")[-1] as Integer
}

private void createChildDevices() {
	state.oldLabel = device.label
     try {
        for (i in 1..2) {
	       addChildDevice("Metering Switch Child Device", "${device.deviceNetworkId}-ep${i}",
		      [completedSetup: true, label: "${device.displayName} (S${i})",
		      isComponent: false, componentName: "ep$i", componentLabel: "Switch $i"])
        }
    } catch (e) {
	    runIn(2, "sendAlert")
    }
}

private sendAlert() {
   sendEvent(
      descriptionText: "Child device creation failed. Please make sure that the \"Metering Switch Child Device\" is installed and published.",
	  eventType: "ALERT",
	  name: "childDeviceCreation",
	  value: "failed",
	  displayed: true,
   )
}

def configuration_model()
{
'''
<configuration>
    <Value type="list" byteSize="1" index="9" label="State of the device after a power failure" min="0" max="1" value="1" setting_type="zwave" fw="">
    <Help>
The device will return to the last state before power failure.
0 - the Dimmer 2 does not save the state before a power failure, it returns to the "off" position
1 - the Dimmer 2 restores its state before power failure
Range: 0~1
Default: 1 (Previous State)
    </Help>
        <Item label="Off" value="0" />
        <Item label="Previous State" value="1" />
  </Value>
      <Value type="list" byteSize="1" index="10" label="First Channel - Operating Mode" min="0" max="5" value="0" setting_type="zwave" fw="">
    <Help>
This parameter allows you to choose the operating mode for the 1st channel controlled by the S1 switch.
Range: 0~5
Default: 0 (Standard)
    </Help>
        <Item label="Standard" value="0" />
        <Item label="Delay On" value="1" />
        <Item label="Delay Off" value="2" />
        <Item label="Auto On" value="3" />
        <Item label="Auto Off" value="4" />
        <Item label="Flashing" value="5" />
  </Value>
        <Value type="list" byteSize="1" index="11" label="First Channel - Reaction For Delay/Auto" min="0" max="2" value="0" setting_type="zwave" fw="">
    <Help>
This parameter determines how the device in timed mode reacts to pushing the switch connected to the S1 terminal.
Range: 0~2
Default: 0 (Cancel)
    </Help>
        <Item label="Cancel" value="0" />
        <Item label="No Reaction" value="1" />
        <Item label="Reset" value="2" />
  </Value>
      <Value type="byte" byteSize="2" index="12" label="First Channel - Time Parameter for Delay/Auto" min="0" max="32000" value="50" setting_type="zwave" fw="">
    <Help>
This parameter allows to set time parameter used in timed modes. 
Range: 0~32000 (0.1s, 1-32000s)
Default: 50
    </Help>
  </Value>
      <Value type="byte" byteSize="2" index="13" label="First Channel - Pulse Time For Flashing" min="1" max="32000" value="5" setting_type="zwave" fw="">
    <Help>
This parameter allows to set time of switching to opposite state in flashing mode.
Range: 1~32000 (0.1s-3200.0s)
Default: 5 (0.5s)
    </Help>
  </Value>
        <Value type="list" byteSize="1" index="15" label="Second Channel - Operating Mode" min="0" max="5" value="0" setting_type="zwave" fw="">
    <Help>
This parameter allows you to choose the operating mode for the 2nd channel controlled by the S2 switch.
Range: 0~5
Default: 0 (Standard)
    </Help>
        <Item label="Standard" value="0" />
        <Item label="Delay On" value="1" />
        <Item label="Delay Off" value="2" />
        <Item label="Auto On" value="3" />
        <Item label="Auto Off" value="4" />
        <Item label="Flashing" value="5" />
  </Value>
        <Value type="list" byteSize="1" index="16" label="Second Channel - Reaction For Delay/Auto" min="0" max="2" value="0" setting_type="zwave" fw="">
    <Help>
This parameter determines how the device in timed mode reacts to pushing the switch connected to the S2 terminal.
Range: 0~2
Default: 0 (Cancel)
    </Help>
        <Item label="Cancel" value="0" />
        <Item label="No Reaction" value="1" />
        <Item label="Reset" value="2" />
  </Value>
      <Value type="byte" byteSize="2" index="17" label="Second Channel - Time Parameter for Delay/Auto" min="0" max="32000" value="50" setting_type="zwave" fw="">
    <Help>
This parameter allows to set time parameter used in timed modes. 
Range: 0~32000 (0.1s, 1-32000s)
Default: 50
    </Help>
  </Value>
      <Value type="byte" byteSize="2" index="18" label="Second Channel - Pulse Time For Flashing" min="1" max="32000" value="5" setting_type="zwave" fw="">
    <Help>
This parameter allows to set time of switching to opposite state in flashing mode.
Range: 1~32000 (0.1s-3200.0s)
Default: 5 (0.5s)
    </Help>
  </Value>
    <Value type="list" byteSize="1" index="20" label="Switch type" min="0" max="2" value="2" setting_type="zwave" fw="">
    <Help>
Choose between momentary and toggle switch.
Range: 0~2
Default: 2 (Toggle)
    </Help>
    <Item label="Momentary" value="0" />
    <Item label="Toggle (Open=On, Closed=Off)" value="1" />
    <Item label="Toggle (On Switch Change)" value="2" />
  </Value>
      <Value type="list" byteSize="1" index="40" label="Reaction to General Alarm" min="0" max="3" value="3" setting_type="zwave" fw="">
    <Help>
This parameter determines how the device will react to General Alarm frame.
Range: 0~3
Default: 3 (Flash)
    </Help>
    <Item label="Alarm frame is ignored" value="0" />
    <Item label="Turn ON after receiving the alarm frame" value="1" />
    <Item label="Turn OFF after receiving the alarm frame" value="2" />
	<Item label="Flash after receiving the alarm frame" value="3" />
  </Value>
      <Value type="list" byteSize="1" index="41" label="Reaction to Flood Alarm" min="0" max="3" value="2" setting_type="zwave" fw="">
    <Help>
This parameter determines how the device will react to Flood Alarm frame.
Range: 0~3
Default: 2 (OFF)
    </Help>
    <Item label="Alarm frame is ignored" value="0" />
    <Item label="Turn ON after receiving the alarm frame" value="1" />
    <Item label="Turn OFF after receiving the alarm frame" value="2" />
	<Item label="Flash after receiving the alarm frame" value="3" />
  </Value>
      <Value type="list" byteSize="1" index="42" label="Reaction to CO/CO2/Smoke Alarm" min="0" max="3" value="3" setting_type="zwave" fw="">
    <Help>
This parameter determines how the device will react to CO, CO2 or Smoke frame. 
Range: 0~3
Default: 3 (Flash)
    </Help>
    <Item label="Alarm frame is ignored" value="0" />
    <Item label="Turn ON after receiving the alarm frame" value="1" />
    <Item label="Turn OFF after receiving the alarm frame" value="2" />
	<Item label="Flash after receiving the alarm frame" value="3" />
  </Value>
      <Value type="list" byteSize="1" index="43" label="Reaction to Heat Alarm" min="0" max="3" value="1" setting_type="zwave" fw="">
    <Help>
This parameter determines how the device will react to Heat Alarm frame.
Range: 0~3
Default: 1 (ON)
    </Help>
    <Item label="Alarm frame is ignored" value="0" />
    <Item label="Turn ON after receiving the alarm frame" value="1" />
    <Item label="Turn OFF after receiving the alarm frame" value="2" />
	<Item label="Flash after receiving the alarm frame" value="3" />
  </Value>
        <Value type="byte" byteSize="2" index="44" label="Flashing alarm duration" min="1" max="32000" value="600" setting_type="zwave" fw="">
    <Help>
This parameter allows to set duration of flashing alarm mode. 
Range: 1~32000 (1s-32000s)
Default: 600 (10 min)
    </Help>
  </Value>
    <Value type="byte" byteSize="1" index="50" label="First Channel - Active power reports" min="0" max="100" value="10" setting_type="zwave" fw="">
    <Help>
The parameter defines the power level change that will result in a new power report being sent. The value is a percentage of the previous report.
Range: 0~100 (1-100%)
Default: 10
    </Help>
  </Value>
    <Value type="byte" byteSize="1" index="51" label="First Channel - Periodic active power and energy reports" min="0" max="120" value="10" setting_type="zwave" fw="">
    <Help>
Parameter 51 defines a time period between consecutive reports. Timer is reset and counted from zero after each report. 
Range: 0~120 (1-120s)
Default: 10
    </Help>
  </Value>
    <Value type="short" byteSize="2" index="53" label="First Channel - Energy reports" min="0" max="32000" value="100" setting_type="zwave" fw="">
    <Help>
Energy level change which will result in sending a new energy report.
Range: 0~32000 (0.01-320 kWh)
Default: 100
    </Help>
  </Value>
      <Value type="byte" byteSize="1" index="54" label="Second Channel - Active power reports" min="0" max="100" value="10" setting_type="zwave" fw="">
    <Help>
The parameter defines the power level change that will result in a new power report being sent. The value is a percentage of the previous report.
Range: 0~100 (1-100%)
Default: 10
    </Help>
  </Value>
    <Value type="byte" byteSize="1" index="55" label="Second Channel - Periodic active power and energy reports" min="0" max="120" value="10" setting_type="zwave" fw="">
    <Help>
Parameter 55 defines a time period between consecutive reports. Timer is reset and counted from zero after each report. 
Range: 0~120 (1-120s)
Default: 10
    </Help>
  </Value>
    <Value type="short" byteSize="2" index="57" label="Second Channel - Energy reports" min="0" max="32000" value="100" setting_type="zwave" fw="">
    <Help>
Energy level change which will result in sending a new energy report.
Range: 0~32000 (0.01-320 kWh)
Default: 100
    </Help>
  </Value>
    <Value type="byte" byteSize="2" index="58" label="Periodic power reports" min="0" max="32000" value="3600" setting_type="zwave" fw="">
    <Help>
This parameter determines in what time interval the periodic power reports are sent to the main controller.
Range: 0~32000 (1-32000s)
Default: 3600
    </Help>
  </Value>
     <Value type="byte" byteSize="2" index="59" label="Periodic energy reports" min="0" max="32000" value="3600" setting_type="zwave" fw="">
    <Help>
This parameter determines in what time interval the periodic energy reports are sent to the main controller.
Range: 0~32000 (1-32000s)
Default: 3600
    </Help>
  </Value>
  <Value type="boolean" byteSize="1" index="28" label="First Channel" value="false" setting_type="zwave" fw="">
    <Help>
Send scene ID on single press 
    </Help>
  </Value>
  <Value type="boolean" byteSize="1" index="fc_2" label="" value="false" setting_type="" fw="">
    <Help>
Send scene ID on double press 
    </Help>
  </Value>
    <Value type="boolean" byteSize="1" index="fc_3" label="" value="false" setting_type="" fw="">
    <Help>
Send scene ID on tripple press 
    </Help>
  </Value>
    <Value type="boolean" byteSize="1" index="fc_4" label="" value="false" setting_type="" fw="">
    <Help>
Send scene ID on hold and release 
    </Help>
  </Value>
  <Value type="boolean" byteSize="1" index="29" label="Second Channel" value="false" setting_type="zwave" fw="">
    <Help>
Send scene ID on single press 
    </Help>
  </Value>
  <Value type="boolean" byteSize="1" index="sc_2" label="" value="false" setting_type="" fw="">
    <Help>
Send scene ID on double press 
    </Help>
  </Value>
    <Value type="boolean" byteSize="1" index="sc_3" label="" value="false" setting_type="" fw="">
    <Help>
Send scene ID on tripple press 
    </Help>
  </Value>
    <Value type="boolean" byteSize="1" index="sc_4" label="" value="false" setting_type="" fw="">
    <Help>
Send scene ID on hold and release 
    </Help>
  </Value>
    <Value type="paragraph" byteSize="1" index="mappings" label="Button Mappings" value="false" setting_type="" fw="">
    <Help>
Toggle Mode
1 pushed - S1 1x toggle
4 pushed - S1 2x toggle
5 pushed - S1 3x toggle

1 held - S2 1x toggle
4 held - S2 2x toggle
5 held - S2 3x toggle

Momentary Mode
1 pushed - S1 1x click
2 pushed - S1 release
3 pushed - S1 hold
4 pushed - S1 2x click
5 pushed - S1 3x click

1 held - S2 1x click
2 held - S2 release
3 held - S2 hold
4 held - S2 2x click
5 held - S2 3x click
    </Help>
  </Value>
    <Value type="boolean" index="enableDebugging" label="Enable Debug Logging?" value="true" setting_type="preference" fw="">
    <Help>
    </Help>
  </Value>
</configuration>
'''
}

Use this for the 2 switches.

/**
 *  Metering Switch Child Device
 *
 *  Copyright 2017 Eric Maycock
 *
 *  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: "Metering Switch Child Device", namespace: "erocm123", author: "Eric Maycock") {
		capability "Switch"
		capability "Actuator"
		capability "Sensor"
        capability "Energy Meter"
        capability "Power Meter"
        capability "Refresh"
        
        command "reset"
	}

	tiles {
		multiAttributeTile(name:"switch", type: "lighting", width: 3, 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 "turningOn", 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"
			}
		}
        valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) {
			state "default", label:'${currentValue} W'
	    }
        valueTile("energy", "device.energy", decoration: "flat", width: 2, height: 2) {
			state "default", label:'${currentValue} kWh'
	    }
        standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		    state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
        }
        standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		    state "default", label:'reset kWh', action:"reset"
	    }
	}
}

void on() {
	parent.childOn(device.deviceNetworkId)
}

void off() {
	parent.childOff(device.deviceNetworkId)
}

void refresh() {
	parent.childRefresh(device.deviceNetworkId)
}

void reset() {
	parent.childReset(device.deviceNetworkId)
}

#9

Oh. and you need this app for the 222.

/**
 *  Virtual Device Sync
 *
 *  Copyright 2018 Eric Maycock (erocm123)
 * 
 *
 *  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.
 *
 *  2018-03-27: Adapted for Hubitat
 */
 
definition(
   name: "Virtual Device Sync",
   namespace: "erocm123",
   author: "Eric Maycock",
   description: "Creates virtual devices and keeps them in sync with the selected physical device. Inteded to be used with devices with multiple relays, SmartLife RGBW, and Fibaro RGBW.",
   category: "Convenience",
   iconUrl: "https://github.com/erocm123/SmartThingsPublic/raw/master/smartapps/erocm123/virtual-device-sync.src/virtual-device-sync.png",
   iconX2Url: "https://github.com/erocm123/SmartThingsPublic/raw/master/smartapps/erocm123/virtual-device-sync.src/virtual-device-sync-2x.png",
   iconX3Url: "https://github.com/erocm123/SmartThingsPublic/raw/master/smartapps/erocm123/virtual-device-sync.src/virtual-device-sync-3x.png"
)

preferences {
    page(name: "setupPage")
    page(name: "createVirtual")
    page(name: "removeVirtual")
    page(name: "removalPage")
    page(name: "createPage")
}

def setupPage() {
    dynamicPage(name: "setupPage", install: true, uninstall: true) {
    section { 
        if(!isVirtualConfigured()){
           input "physical", "capability.switch", title: "Which Physical Switch", multiple: false, required: true, submitOnChange: true
           if(physical != null){
              paragraph "Device Handler: $physical.typeName\r\n\r\nDetected Number of Endpoints: ${getEndpoints()}\r\n\r\nRecommended Type: ${getType()}"
              input "virtualSwitchType", "enum", title: "Virtual Switch Type", value: getType() , multiple: false, required: true, options: ["Switch","Energy Switch","Dimmer"]
              app.updateSetting("virtualSwitchType", getType())
           }
           href "createVirtual", title:"Create Virtual Devices", description:"Create virtual devices"
        }else{
           def switchNames = ""
           getChildDevices().each {
               switchNames = switchNames + it.displayName + "\r\n"
           }
           paragraph "Chosen Device: $physical\r\n\r\nTo change to a different device, please remove the virtual devices below."
           paragraph "Device Handler: $physical.typeName\r\n\r\nDetected Number of Endpoints: ${getEndpoints()}\r\n\r\nRecommended Type: ${getType()}\r\n\r\nVirtual Switches have been created. They will be kept in sync with the physical switch chosen above\r\n\r\n$switchNames"
           href "removeVirtual", title:"Remove Virtual Devices", description:"Remove virtual devices"
        }
    }
    section([title:"Available Options", mobileOnly:true]) {
            input "setLabel", "boolean", title: "Change the default name of the app?", required: false, submitOnChange: true, value: false
            if (settings.setLabel != null && setLabel.toBoolean() == true) {
               label title:"Assign a name for your app (optional)", required:false
            } 
		}
    }
}

def createVirtual(){
   dynamicPage(name: "createVirtual", title: "Associate your device's endpoints with virtual devices", nextPage: "createPage") {
		section {
			paragraph "This process will create virtual devices and associate them with the endpoints on your physical device."
            def switchNames = ""
            for (int i = 1; i <= getEndpoints(); i++){
               
               if ((physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0) ||
               (physical.typeName.toUpperCase().indexOf("SMARTLIFE") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0)){
                  switchNames = switchNames + "$physical.displayName - ${getColor(i, "upper")} Channel\r\n"
               } else {
                  switchNames = switchNames + "$physical.displayName - ${i}\r\n"
               }
            }
            paragraph "The following switches will be created:\r\n\r\n" + switchNames
		}
    }
}

def createPage(){
   dynamicPage(name: "createPage", title: "Devices have been created", nextPage: "setupPage", createVirtualDevice())
}

def removeVirtual(){
   def switchNames = ""
   dynamicPage(name: "removeVirtual", title: "Remove the virtual switches created by this app", nextPage: "removalPage") {
		section {
			paragraph "This process will remove the virtual switches created by this program. Press next to continue"
            getChildDevices().each {
               switchNames = switchNames + it.displayName + "\r\n"
            }
            paragraph "The following virtual switches will be removed:\r\n\r\n" + switchNames
		}
    }
}

def removalPage(){
   dynamicPage(name: "removalPage", title: "Devices have been removed", nextPage: "setupPage", removeVirtualDevice()) 
}

def createVirtualDevice() {
    if(!isVirtualConfigured()){
       def switchName
       try {
          for (int i = 1; i <= getEndpoints(); i++){
             if ((physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0) ||
             (physical.typeName.toUpperCase().indexOf("SMARTLIFE") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0)){
                switchName = "$physical.displayName - ${getColor(i, "upper")} Channel\r\n"
             } else {
                switchName = "$physical.displayName - ${i}\r\n"
             }
             def switchType = ""
             if (virtualSwitchType != null && virtualSwitchType == "Switch"){
                switchType = "Simulated Switch"
             } else if (virtualSwitchType != null && virtualSwitchType == "Energy Switch") {
                switchType = "Simulated Energy Switch"
             } else {
                switchType = "Simulated Dimmer"
             }
             def child = addChildDevice("erocm123", switchType, getDeviceID(i), null, [name: getDeviceID(i), label: switchName, completedSetup: true])
          }   
       } catch (e) {
          return {
		   section {
			   paragraph "Error when creating the virtual devices. Make sure that you have all of the \"Simulated\" device handlers installed."
		   }
       }
    }
    return {
	   section {
	      paragraph "Devices have been configured. Press next to go to the main page."
	   }
    }
    }else{
       return {
	      section {
		     paragraph "Devices have already been configured."
		  }
       }
    }
}

def isVirtualConfigured(){ 
    def foundDevice = false
    getChildDevices().each {
       foundDevice = true
    }
    return foundDevice
}

def removeVirtualDevice() {
    try {
       unsubscribe()
       getChildDevices().each {
          deleteChildDevice(it.deviceNetworkId)
       }
       return {
          section {
	         paragraph "Devices have been removed. Press next to go to the main page."
	      }
       }
	} catch (e) {
       return {
          section {
			paragraph "Error: ${(e as String).split(":")[1]}."
	      }
       }
    }
}

private getDeviceID(number) {
    return "${app.id}-${number}"
}

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

def updated() {
  log.debug "Updated with settings: ${settings}"
  if(physical != null && setLabel != null && setLabel.toBoolean() != true){
     app.updateLabel("Virtual Device Sync - ${physical.label ? physical.label : physical.name}")
  }
  unsubscribe()
  initialize()
}

def initialize() {
  log.debug "Initializing Virtual Device Sync"
  if ((physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0) ||
  (physical.typeName.toUpperCase().indexOf("SMARTLIFE") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0)){
     subscribe(physical, "red", physicalHandler)
     subscribe(physical, "blue", physicalHandler)
     subscribe(physical, "green", physicalHandler)
     subscribe(physical, "white", physicalHandler) // Added for Fibaro RGBW Controller
     subscribe(physical, "white1", physicalHandler)
     subscribe(physical, "white2", physicalHandler)
     subscribe(physical, "redLevel", physicalHandler)
     subscribe(physical, "blueLevel", physicalHandler)
     subscribe(physical, "greenLevel", physicalHandler)
     subscribe(physical, "whiteLevel", physicalHandler) // Added for Fibaro RGBW Controller
     subscribe(physical, "white1Level", physicalHandler)
     subscribe(physical, "white2Level", physicalHandler)
  } else {
     for (int i = 1; i <= getEndpoints(); i++){
        subscribe(physical, "switch${i}", physicalHandler)
        subscribe(physical, "power${i}", powerHandler)
        subscribe(physical, "energy${i}", energyHandler)
     }
  }
  getChildDevices().each {
     subscribe(it, "switch", virtualHandler)
  }
}

def virtualHandler(evt) {
    log.debug "virtualHandler called with event: deviceId ${evt.deviceId} name:${evt.name} source:${evt.source} value:${evt.value} isStateChange: ${evt.getIsStateChange()} isPhysical: ${evt.isPhysical()} isDigital: ${evt.isDigital()} data: ${evt.data} device: ${evt.device}"
    getChildDevices().each {
        if ("${evt.deviceId}" == "${it.id}") {
          def switchNumber = it.deviceNetworkId.split("-")[1]
          if ((physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0 && !(physical.typeAuthor.toUpperCase().indexOf("LOMAS") >= 0)) ||
          (physical.typeName.toUpperCase().indexOf("SMARTLIFE") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0)){
             switch (evt.value){
                case "setLevel":
                   physical."set${getColor(switchNumber.toInteger()).capitalize()}Level"(it.currentValue("level"))
                break
                default:
                   physical."${getColor(switchNumber.toInteger())}${evt.value.capitalize()}"()
                break
             }
          } else if (physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0) {
             switch (evt.value){
                case "setLevel":
                   physical."setLevel${getColor(switchNumber.toInteger()).capitalize()}"(it.currentValue("level"))
                break
                default:
                   physical."${evt.value}${getColor(switchNumber.toInteger()).capitalize()}"()
                break
             }
          } else {
             switch (evt.value){
                case "setLevel":
                   physical."setLevel${switchNumber}"(it.currentValue("level"))
                break
                default:
                   physical."${evt.value}${switchNumber}"()
                break
             }
          }
       }
    }
}

def physicalHandler(evt) {
  log.debug "physicalHandler called with event:  name:${evt.name} source:${evt.source} value:${evt.value} isStateChange: ${evt.getIsStateChange()} isPhysical: ${evt.isPhysical()} isDigital: ${evt.isDigital()} data: ${evt.data} device: ${evt.device}"
  if ((physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0) ||
  (physical.typeName.toUpperCase().indexOf("SMARTLIFE") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0)){
     switch(evt.name){
        case ~/.*Level.*/:
           getChildDevice("${app.id}-${getSwitchNumber(evt.name)}").extSendEvent([name:"level", value:"$evt.value", type:"physical"])
        break
        default:
           getChildDevice("${app.id}-${getSwitchNumber(evt.name)}").extSendEvent([name:"switch", value:"$evt.value", type:"physical"])
        break
  }
  } else {
     switch(evt.name){
        case ~/.*Level.*/:
           getChildDevice("${app.id}-${getSwitchNumber(evt.name)}").extSendEvent([name:"level", value:"$evt.value", type:"physical"])
        break
        default:
           getChildDevice("${app.id}-${getSwitchNumber(evt.name)}").extSendEvent([name:"switch", value:"$evt.value", type:"physical"])
        break
     }
  }
}

def powerHandler(evt) {
   log.debug "powerHandler 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}"
   getChildDevice("${app.id}-${getSwitchNumber(evt.name)}").extSendEvent([name:"power", value:"$evt.value"])
}

def energyHandler(evt) {
   log.debug "energyHandler 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}"
   getChildDevice("${app.id}-${getSwitchNumber(evt.name)}").extSendEvent([name:"energy", value:"$evt.value"])
}


private getColor(number, format = null){
   switch (number) {
      case 1:
         if(format == "upper") return "R" else if(format == "lower") return "r" else return "red"
      break
      case 2:
         if(format == "upper") return "G" else if(format == "lower") return "g"  else return "green"
      break
      case 3:
         if(format == "upper") return "B" else if(format == "lower") return "b"  else return "blue"
      break
      case 4:
         if (physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0){
            if(format == "upper") return "W" else if(format == "lower") return "w"  else return "white"
         } else {
            if(format == "upper") return "W1" else if(format == "lower") return "w1"  else return "white1"
         }
      break
      case 5:
         if(format == "upper") return "W2" else if(format == "lower") return "w2"  else return "white2"
      break
   }
}

private getSwitchNumber(value){
   switch (value) {
      case ~/.*red.*/:
         return 1
      break
      case ~/.*green.*/:
         return 2
      break
      case ~/.*blue.*/:
         return 3
      break
      case ~/.*white1.*/:
         return 4
      break
      case ~/.*white2.*/:
         return 5
      break
      case ~/.*white.*/:
         return 4
      break
      case ~/.*switch.*/:
         return value.substring(6).toInteger()
      break
      case ~/.*energy.*/:
         return value.substring(6).toInteger()
      break
      case ~/.*power.*/:
         return value.substring(5).toInteger()
      break
   }
}

private getEndpoints() {
   def endpoints = 0
   if (physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0){
      endpoints = 4
   } else if (physical.typeName.toUpperCase().indexOf("SMARTLIFE") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0){
      endpoints = 5
   } else {
      physical.supportedCommands.each {
         switch (it) {     
            case ~/.*on.*/:
               for (int i = 1; i <= 10; i++){
                  if (it.toString().indexOf("$i") >= 0) if (i > endpoints) endpoints = i
               }
            break
         }
      }
   } 
   return endpoints
}

private getType() {
   String hasCapability = ""
   
   if (physical.hasCapability("Switch")) {
      hasCapability = "Switch"
   }
   if ((physical.hasCapability("Power Meter")) || (physical.hasCapability("Energy Meter"))) {
      hasCapability = "Energy Switch"
   }
   if (physical.hasCapability("Switch Level")) {
      hasCapability = "Dimmer"
   }
   if ((physical.typeName.toUpperCase().indexOf("FIBARO") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0) ||
   (physical.typeName.toUpperCase().indexOf("SMARTLIFE") >= 0 && physical.typeName.toUpperCase().indexOf("RGBW") >= 0)) {
      hasCapability = "Dimmer"
   }
   
   
   return hasCapability
}

#10

Thanks. And for the 221? Same as the 222 or 223?


#11

I haven't got a 221. Is this still 2 switches?
If it's not, try the 222 driver but don't use the Virtual Sync driver.

Are you happy with what you need to do for the 222 and 223 to get them working?


#12

Yes is 2 switches. I think the older version. In ST is working well but using a different controller than the 223

Not surw yet. I have about 60 devices to migrate from Smartthings to HE. Once I gather all I will start trying. I do not want to do transition before I am sure all my devices will work in HE. Probably once I try I will have questions. Thanks a lot


#13

No worries. Try the 222 driver for the 221. Fingers crossed it will work.


#14

I have successfully installed the FGS-222 in my FGS-221 and it worked well using the Virtual Device Sync app.

Now for the FGS-223 I have installed the Device handler "Fibaro Double Switch 2 FGS-223" and as well the device handler you told me "Metering Switch Child Device" so how should I use them ?? I guess I have to use the first one as the device handler in the device itself but how to use the Metering switch child device ?? is there an app similar to the 222 or no app required on this one??


#15

If memory serves me you need to first define the 223 handlers and the the metering handler.
Then pair your 223 and it should automatically define the devices as per this screenshot. Was a while ago though.


#16

by "define" you mean copy-paste the code you gave me and have the drivers available and ready to use?? then when pairing automatically the device will pick those ?


#17

Yes. Spot on. I'm sure that is how I did it but it was a while ago and my little grey cell is not as good as it used to be. :wink:


#18

what I did is first add to hubitat the device and then add the code from the drivers you gave me and later try to chose from the drop down list so let me exclude it and include it again to see if it automatically gets the drivers assigned


#19

@bobbles It did not work

Hi @ericm I see you are the owner/developer of the drivers for the FGS-223. Can you advise how to use them ?? @bobbles has provided the "Fibaro Double Switch 2 FGS-223" and the "Metering Switch Child Device" but how to use them ?? if I assign as the driver of my fibaro switch the "Fibaro Double Switch 2 FGS-223" it will work to turn on/off both switches together but is not creating the child devices. Is there an app or something that should be used ??
thanks


#20

@ericm @bobbles you can ignore my previous post, I found the problem and is that I changed the name of the "Metering Switch Child Device" to something different in order to identify that it is for the FGS-223 so once I returned it to original name it created the 2 child devices. thanks