New starter - help needed on Fibaro and Wall Switches

Just received my Hubitat box today and excited to get started. I've spent a few hours trawling the community forum and Github, but am not finding a solution for what I'm trying to do - any advice welcomed!

I have a Fibaro FGS-223 ("Double Switch 2") that I want to control. I found the post pointing to the following driver code:

...which also mentions the need for the "Metering Switch Child Device. I have installed both into the Drivers Code section of the Hubitat interface. A few things I'm confused about...

  1. How is the Metering Switch Child Device used? I don't understand the link between the 2
  2. If i try to use something like Virtual Device Sync to create virtual switches, the Fibaro shows up with zero end points, so zero virtual switches are created
  3. I am unable to trigger the Fibaro using Ruler Machine or any other apps, say for example by triggering at a certain time.

I'm thinking I've done something wrong with the device code install or something. Has anyone seen a successful and documented use of the FIbaro 223?

Thanks in advance from a new guy who's excited to move away from SmartThings :slight_smile:

Chris

1 Like

Did you get this working?

Or Anyone else able to help? Having the same issue here. Main Switch installs and then shows the 2 switches under in Devices. But both show status as UNKNOWN without any option to do anything different?

I made a bit more progress than I described initially, but still don’t have it working.

I did finally get the child switches to show - if I recall, the solution to this was to hit the configure option (in the Hubitat controls) of the master switch and to actually go through and enter values for every field, regardless of whether I wanted to use that function. For example - I had previously not bothered to add a value for dimming behaviour or flashes, because I’m not using those features.

For whatever reason that seemed to do the trick in terms of getting the child switches to appear, but the status for those devices shows as Unknown no matter what I do. See screenshot - ignore that the master is Inactive - it’s currently unplugged but reconnecting makes no difference to the Unknown status of the child switches.

I have resigned myself to waiting for official support of dual switches to come from a system update. However I am starting to wonder if there is a technical reason why this can’t be done and so won’t happen - I don’t see any other vendor dual switch units supported (e.g. Aeotec seem very well supported, but their dual switches are not listed either). I asked a question about this on the Hubitat subreddit with no answer, even though I know it’s regularly frequented by a Hubitat employee. No news = bad news? Even a generic driver for dual switches would be helpful.

Good luck with your efforts. I will post if I find a solution, but I am not hopeful. Back to SmartThings maybe :slightly_frowning_face:

Use this driver to get S1 and S2 switches to appear.

/**
 *  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
}

Hi

Thats both - I've tried using the code @bobbles although I thought it's an app not a driver, doesn't compile as a driver.

As you can see, I can see both in the devices, but they list as unknown which means they then don't work and the virtual device sync can't see them as endpoints?

Any further help would be really appreciated, as this is the first of a signficant number I need.

Oops. My mistake.
I'm using the FGS222 driver on a 223 device.
I think this was because either there wasn't a 223 DH when I purchased it or I couldn't get it to work.
Either way I'm using a 222 driver with the app I posted earlier and I can control both switches.

Do you have a link/code for the 222 driver you're using so I can make sure it's the same code

Thanks - appreciate the help.

Here is the code I'm using.

/**
 *  
 *	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)
}
2 Likes

EDIT - Fixed by using the 222 Code above, also then needed to add the Simulated Switch driver - that then allowed me to remove the error when it detected but didn't add.


Ok, so new problem, and this may be the wiring. It's now showing as "On" which is good. Issue is that nothing is happening, nothing has also happened when I use the physical switch either.

I'm trying to wire as follows:

Cable from an inside light switch (3 wires, L/E/N)

SW1 - Pump (and should switch on/off with the inside light switch)
SW2 - Light (virtual switch, so will programme to come on/off when it's dark etc)

Wiring:

E - all Earths joined together
N - all Neutral joined together and 1 wire to Fibaro
L - Live from cable to L in Fibaro
S1 - Live pump wire
S2 - Live light wire
Q1/Q2 - left empty

Should Q1 and Q2 have anything in them as I don't have load wires. Previously it has worked by connecting the light switch wire to the pump wire direct (3 to 3)

Appreciate any support.

Here's the manual which gives wiring instructions.

Got it working now - thank you.

@chris.feltham - happy to talk you through the steps I've done as we had a similar issue to begin with

Good stuff. Glad you got there in the end.

@jaedgar - glad you got it working!

I on the other hand am obviously still doing something dumb.

I removed the existing S1 & S2 child devices that I (somehow) managed to get displayed, but which were showing Inactive per previous screenshot.

I installed the 222 driver from @bobbles for the Fibaro 223 device

...and have loaded the Virtual Device Sync code as an app. However when I run the Virtual Device Sync, to create the child devices I get an error. I select the 223 device (with 222 driver) and it correctly identifies that there are 2 endpoints...

...but when I hit next, I get the following error:

You mentioned Simulated Switch device handler.....what is that? I'm clearly missing a step somewhere but can't figure it out.

I previously had used the "Metering Switch Child Device" driver code, but can't for the life of me figure out how they all fit together.

Can you go step by step what you did to get this working?

Thanks
Chris

See post 4.

Hi Chris

Yes mate, so overall I needed the 222 driver, metered service, the virtual app and simulated switch.

Simulated switch from the same driverbase on GitHub, don’t have a link to hand as away at the moment but from the same as the 222/223 and Metered Child.

With ALL those installed:

222 - then configure.
Then in the virtual app you can create the switches.

You had the same error I did before installing the simulated switch driver. So grab that and I think it’ll work for you.

@bobbles - doesn’t seem to work in rule machine making on/off dependant on Sunset / Sunrise - going to look in detail but wanted to check you had working and wasn’t a known bug

If you can’t find the code I’ll link it when I’m back on Sunday

Eureka - that did it :+1:

In reading through the instructions I'd picked up on the need for 3 items (driver, virtual switch, metering), but had not realised the simulated switch was a 4th piece of the puzzle. I now have the Fibaro working correctly, with Ruler Machine able to control lights attached.

Thanks both for the pointers!

Chris

1 Like

Hey! I'm using the "FGS-222 method" outlined above for my FGS-223. It mostly works fine, but sometimes it's as if the virtual devices goes to "sleep". They don't react to commands anymore, until I do something with the "real" parent device - turn off, on etc. Then the virtual ones works again, for a while.

Hard to explain, but TL;DR I find it a bit unreliable. Is anyone else experiencing the same thing? Any luck getting the "real" FGS-223 driver (Fibaro FGS-223) going? I tried that one as well, but couldn't get it to work.

Hi everyone

I have a number of these Fibaro FGS-223 here and after an initial migration from STT I have been using the Dual Relay Driver to get each endpoint operational. However I then loose power & physical/digital capabilities. I see you have been having some success with the FGS-222 driver with a virtual app to create the child switches and then metering.

Having read this thread twice I am still confused as to how to get these switches working with more functionality?

Also shout out for @ericm as he sent me a link to his HE-ported device drivers from the STT platform.

Can anyone write a defacto guide to getting these FGS-223 switches working on HE please?
I changed the parent device type to @ericm 's driver and created the children but then the endpoints didnt work. Do I need something else?

Do you have the custom DTH from ST that you were using for the double switch 2? If so, you could port it yourself using the single switch 2 driver as a template. If you have the DTH for the 213 (ss2) then you'd really have something easy. You'd just have to compare the driver to the DTH and then make the same changes on the DS2.