Homekit Beta - Child Device Support

I think most people if not all are using this driver.
Your help will be very much appreciated.

Summary

/**

  • 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 "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())
}
}

private getCommandClassVersions() {
[
0x20: 1, // Basic
0x25: 1, // Switch Binary
0x70: 2, // Configuration
0x98: 1, // Security
0x60: 4, // Multi Channel
0x8E: 2, // Multi Channel Association
0x26: 1, // Switch Multilevel
0x87: 1, // Indicator
0x72: 2, // Manufacturer Specific
0x5B: 1, // Central Scene
0x32: 3, // Meter
0x85: 2, // Association
0x86: 1, // Version
0x9B: 1, // Association Command Configuration
0x90: 1, // Energy Production
0x73: 1, // Powerlevel
0x30: 1, // Sensor Binary
0x28: 1, // Switch Toggle Binary
0x2B: 1, // Scene Activation
0x75: 2 // Protection
]
}

def parse(String description) {
//log.debug description
def result =
def cmd = zwave.parse(description, commandClassVersions)
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.multichannelv4.MultiChannelCapabilityReport cmd)
{
//log.debug "multichannelv4.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.multichannelv4.MultiChannelCmdEncap cmd) {
//logging("MultiChannelCmdEncap ${cmd}")
def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions)
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,0] == 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.multiChannelV4.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd)
} else {
cmd
}
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
//log.debug cmd
def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) // 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,0])
   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()
{
'''



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)






This parameter allows you to choose the operating mode for the 1st channel controlled by the S1 switch.
Range: 0~5
Default: 0 (Standard)










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)







This parameter allows to set time parameter used in timed modes.
Range: 0~32000 (0.1s, 1-32000s)
Default: 50




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)




This parameter allows you to choose the operating mode for the 2nd channel controlled by the S2 switch.
Range: 0~5
Default: 0 (Standard)










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)







This parameter allows to set time parameter used in timed modes.
Range: 0~32000 (0.1s, 1-32000s)
Default: 50




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)




Choose between momentary and toggle switch.
Range: 0~2
Default: 2 (Toggle)







This parameter determines how the device will react to General Alarm frame.
Range: 0~3
Default: 3 (Flash)








This parameter determines how the device will react to Flood Alarm frame.
Range: 0~3
Default: 2 (OFF)








This parameter determines how the device will react to CO, CO2 or Smoke frame.
Range: 0~3
Default: 3 (Flash)








This parameter determines how the device will react to Heat Alarm frame.
Range: 0~3
Default: 1 (ON)








This parameter allows to set duration of flashing alarm mode.
Range: 1~32000 (1s-32000s)
Default: 600 (10 min)




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




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




Energy level change which will result in sending a new energy report.
Range: 0~32000 (0.01-320 kWh)
Default: 100




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




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




Energy level change which will result in sending a new energy report.
Range: 0~32000 (0.01-320 kWh)
Default: 100




This parameter determines in what time interval the periodic power reports are sent to the main controller.
Range: 0~32000 (1-32000s)
Default: 3600




This parameter determines in what time interval the periodic energy reports are sent to the main controller.
Range: 0~32000 (1-32000s)
Default: 3600




Send scene ID on single press




Send scene ID on double press




Send scene ID on tripple press




Send scene ID on hold and release




Send scene ID on single press




Send scene ID on double press




Send scene ID on tripple press




Send scene ID on hold and release




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







'''
}

1 Like

And just to confirm from a quick scan of this page which seems to be the original from your message, the power readings do not show in the child device? The only thing the child devices do is on/off?

And I assume you have the metering switch child device installed from here ?

This is how the child device looks like:


It does show power metering.

True

I think that the problem it has with Homekit integration is that it uses the Metering child device driver which is not made by Hubitat. I think the guy who wrote this created the child driver in order to utilize the power metering which was not available in the native child device driver by Hubitat (Generic Component Switch).
I can only speak to myself but I do not really use the power metering and I will be happy with a solution that utilizes the Generic Component Switch for the child devices. If Hubitat as a Generic Component Switch with metering option this is awesome but I am not sure there is one.

Just found out that Hubitat has the Generic Component Metering Switch but I am not sure if this is the right one

I have no idea how much debugging I may need to do here, but Ive forced the basics over to the standard generic child device with the code here

You'll need to change the device handler for the parent to this one, and then you'll need to change the child devices to the Generic Component Switch. Let me know how you get on.

2 Likes

Thanks for looking into this @cjcharles . :+1:t2:

Im not sure it does actually.... and there is nothing published on their main github page or documentation (only the one with contributions, so may not be fully supported). Either way, there is a reasonable chance that the native way Hubitat works will allow this information to display in the Generic Switch device handler, even if it doesnt have a proper attribute for it.

1 Like

Thank @cjcharles ,
As it is becoming very late in my location (or should I say early morning :slight_smile: ) I will have to test and report tomorrow.

1 Like

Cool, Im sure we can get it working fairly easily as it still uses component/children in the same way.

For the sake of getting the driver properly listed on the forum I have republished the driver on my Fibaro FGS 22x page

I've changed the driver for the 223 to your modified one (and just appended the name in the code with 'CJ' so I can easily differentiate). I also changed the child drivers to Generic Component Switch.

Using 'On' 'Off' on the parent device switches both channels On/Off. However if I try and use the 'On' 'Off' in the child device it throws an error as shown.

I've deleted the child devices and in the parent device done a configure. That recreated the two child devices and automatically assigned the 'Generic Component Switch'. However they are still not controllable and throw the error:

1 Like

Sorry I was lazy, missed the definition. Have updated that now so maybe give it another go please? If not I'll do it properly at the weekend when I'm not tired after work.

1 Like

No problem I’ll give it a try and report back. Thanks

Edit: I'm getting the same error unfortunately. I've pasted the new driver over the old and saved, deleted the child devices. Configure on the parent recreates the two generic component switches but an On from the device gives the following output:

Shouldn't need to remove the children but I spotted the original driver used a slightly different command definition so have swapped over to the hubitat standard and that hopefully will get it working! But coding at midnight is never good!

2 Likes

@cjcharles - midnight coding worked. Thank you!

The two endpoints are now switchable independently and no longer log errors. Although its now using the Generic Component Switch rather than the Metering Switch Child Device, it does show power in the current states while On:

After the switch is turned off, 'power' goes to 0, then when I return to the device page later, the 'power' heading is no longer shown in current states (but is restored if I switch the device back on). That doesn't affect my use and I'm not sure whether that 'power : 0' should be persistent in Current States or not, but thought I'd mention it.

If you do update this on HPM, you might want to rename it in the code - 'Fibaro Double Switch' is the old Z Wave device, this Z Wave Plus one is 'Fibaro Double Switch 2'

@amithalp - I can confirm the individual child devices now show under "Switches' in the Hubitat Homekit beta app:

...and it all works from the Home app.

Thanks again @cjcharles

2 Likes

Good to hear. Will add to HPM with the updated name so its easy for people to find.

I dont have control of the child device persisting the power information as it is a Default device built into Hubitat, but the power information is still there in the parent as I understand it, so hopefully all ok.

2 Likes

I tested the new driver and did this process:
Installed using HPM - OK
Assigned the new driver to an existing parent device - OK
Manually changed the child devices from Metering Switch Child Device to Generic Component switch - had to be done manually - OK
Assigned the child device to Homekit integration - OK

All in all, a great job done. Thank you very much @cjcharles

3 Likes

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