Common Composite (Child) Device Handlers

I ended up making a complete set of Child Device handlers for my ST_Anything project, using my own namepsace and stored in my Github repository. I did this primarily to allow users of my project to simply use the ST IDE Github integration for ST_Anything, making adding and maintaining the custom DH’s much simpler for everyone. Feel free to take a look at my parent and child device handlers to see how I have chosen to implement the Composite Device Handler. I implemented an automatic child creation mechanism within the parent DH, which allows the user to customize their Arduino sketch to include whatever “devices” they’d like and have the corresponding children automagically created. Users of ST_Anything do NOT need to customize the DH groovy code at all (unless they want to customize the child devices to their liking.)

1 Like

this one is a bit complicated for my level. is there a step by step on how to install it. I have tried installing ST_Anything aswellwith the documented Child Device handlers but didnt win at all

The ReadMe on my Github is a set of step by step instructions.

What are you trying to accomplish? Knowing that would help us to be able to help you.

I bought this device Fibaro Double Switch 2 fgs-223 EU v3.2 and trying to get it to work with my smartthings hub. I have a double switch on my wall controliing two light. in short am trying to have a one switch that splits into two once i enter it. eg kitchen------->switch1---------Switch2

SmartThingsPublic/devicetypes/erocm123/metering-switch-child-device.src/metering-switch-child-device.groovy this was probably the closest but couldnt get the parent to call it out.

Since you’re trying to use a Fibaro device, neither my code nor @erocm1231’s is going to work without some changes.

Personally, I would recommend you start with the example code highlighted in the following post. This appears to be very similar to what you’re trying to accomplish.

thank your for your reply this worked. it shows 5 channels at the moment and am only using the first two. thank you.

1 Like

I have a handler created for that device that uses the composite device scheme. Have you tried it?

Also, I’m really surprised that putting the namespace in the addChildDevice call didn’t work. In my testing, it allowed me to use other people’s namespaces:

addChildDevice("erocm123", "Metering Switch Child Device", "${device.deviceNetworkId}-ep${i}", null,
[completedSetup: true, label: "${device.displayName} (Q${i})",
isComponent: false, componentName: "ep$i", componentLabel: "Output $i"])
1 Like

thank you for the time you put working in these things. I tried your code earlier but it didnt work I tried it now again and its working just fine. do youhave any thing for the fibaro roller shutter fgr222eu2.5

Sorry, I do not.

Eric, the child device handlers have stopped working. I installed your Child Switch as a child to Sinope. Sinope did correctly create the child switch. However, when the switch issues the parent.childon(…) I get an error that parent is null. The childon(…) method is correctly installed in the parent, and in the simulator I can trigger it and get the right reaction. But toggling the switch does not call the childon(…) in the parent.

Are you aware of what might be going on?

If you remove the device and re-create them does it change anything?

Did it just stop working for you? Or have you just noticed it? Which of the device handlers are you using? I have found that quite a few of Eric’s handlers have been published, so perhaps some of them have changed as a result, but I dont think that is recent.

I was using the Switch Child Device with the Sinope Device Handler. But I
also tested the problem with a very simple switch child and a very simple
parent which just printed debug statements.

In the Child, the statement parent.childon(dni) triggers the debug
statement that parent is null. The device clearly shows my parent device as
the parent, and the parent device clearly shows the Child Device as a
child. Therefore, the statement parent.childon(dni) never triggers the
method childon(dni) in the parent. There is no communication between parent
and child.

Eric has updated his Switch since last I used it, so it is something in the
architecture which does not recognize the keyword parent or child.

I got my new devices working by restructuring the switch as a standalone
(i.e. not a child) and using a SmartApp to establish the communication.
But, as best I can see, the concept of a Composite Device does not work at
this time due to the keywords not being recognized.

I would probably suggest you post the exact code that is used in the web IDE as the easiest way to investigate (if you have github integration then be aware this can sometimes make it hard to ensure you are using a specific version of a DTH). I have parent child relationships in 6 or so different devices (several of which I wrote) and have no problems, so without seeing the code (and maybe attempting to reproduce it, it will probably be hard to go any further).

OK, here is what you asked for. For testing purposes I created a device handler named Test Child DH which does nothing but call the parent,childon(dni) method. I put in lots of debugging statements so that you can see what is happening. Then I created both a device handler named Test Parent DH and a device named TestParent which uses this device handler. The device does nothing more than create the child and provide some debug messages. In particular, the parent method childon(dni) throws a trace message that it has been reached. These were published to me and showed up in the Smartthings phone app.

I set the IDE to Live Logging, and triggered the TestChild on the phone app. Tthe debug statements verified that the parent was indeed the TestParent, that the dni was correct and the parent.childon(dni) was called. However, the Live Debugging also revealed that the TestParent’s childon(dni) method was NOT invoked. This is the failure I have been describing.

Hopefully I simply missed a step. The two device handlers are listed below as you had requested.

Parent device handler:

/**

  • Test Parent DH
  • Copyright 2017 RALPH MILLER
  • 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: “Test Parent DH”, namespace: “rlmillerx”, author: “RALPH MILLER”) {
capability "Switch"
capability "Sensor"
capability “Actuator”
}
}

preferences {

    command "childon", ["string"]
    command "childoff", ["string"]
         
}

simulator {
    
}

tiles (scale: 2) {

   standardTile("thermPresence", "device.switch", inactiveLabel: false, width: 3, height: 3) { //, decoration: "flat"
		state "on", label:'Home', action:'off', backgroundColor: "#44b621", icon: "st.Home.home2", nextState:"Updating"
		state "off", label:'Away', action:'on', backgroundColor: "#ADD8E6", icon: "st.Home.home2", nextState:"Updating"
        state "Updating", label:"Updating", backgroundColor: "#ffffff", icon: "st.secondary.secondary" 	//, defaultState: true
        
	}

}

def installed() {
initialize()
}

def updated() {
initialize()
}
def initialize() {
// create child switch
// first check if child device is already installed
def childDevices = getChildDevices()
log.debug "${childDevices}"
log.debug "${childDevices.size()}"
if (childDevices.size() == 0) {
log.trace "Creating Child"
try {
addChildDevice(“Test Child DH”,“testchild”,null, [completedSetup: true, label: “TestChild”, isCompenent: false, componentLabel: “TestChild”])
} catch(Exception e) {
log.debug “${e}”}
log.trace “Child created”
}
}
// handle commands
def parse(String description) {
log.debug “parse: description = ${description}”

}

def on() {
log.trace "on command received"
sendEvent(name: “switch”, value: “on”)
}

def off() {
log.trace "off command received"
sendEvent(name: “switch”, value: “off”)
}

// Test child integration
void childon(x){
log.debug “On command from ${x}”
}
void childoff(x){
log.debug “Off command from ${x}”
}

Child device handler:

/**

  • Test Child DH

*/
metadata {
definition (name: “Test Child DH”, namespace: “rlmillerx”, author: “Ralph Miller”) {
capability "Switch"
capability "Actuator"
capability "Sensor"
capability “Refresh”
}

tiles {
	standardTile("controller", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"on"
		state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"off"
						}
	}

}

void on() {
log.trace "Child was turned on"
try {
log.debug " Parent is ${parent.name}"
log.debug "Device ID is ${device.deviceNetworkId}"
parent.childOn(device.deviceNetworkId)
log.debug “parent.childOn was just called”
} catch(Exception ex) {
log.debug “Exception ${ex}”
}
}

void off() {
log.trace "Child was turned off"
try {
log.debug "Parent is ${parent.name}"
log.debug "Device ID is ${device.deviceNetworkId}"
parent.childOn(device.deviceNetworkId)
log.debug “parent.childOn was just called”
} catch(Exception ex) {
log.debug “Exception ${ex}”
}
}

@rlmillerx
Can you edit the post and use the pre-formatted text flags, that way it appears correctly and will be easier to check in my own IDE. From a quick glance I agree it does look like it should be working and print the ‘On/Off command from x’ message. Unless something is failing before that to mean it never gets called.

Two suggestions:

  1. It is likely that the SmartThings team may have created a device called testchild while they are doing their own testing. If they happen to have done some craziness with publishing it publically (quite likely) then it may not be using the child device handler you have created or the right child. I would suggest picking a slightly less common name or adding a number at the end for example. I would also probably ensure you specify the namespace as the first parameter since they could also have a public DTH called ‘Test Child DH’…
  2. I would try adding the hub as location, rather than null. I have found null sometimes doesnt work well for me, but using the correct location helps. Instead of the ‘null’ part use ‘device.hub.id

I tried both your suggestions and they made no difference. So here are the revised files using the pre-formatted text flag.

/**
  • Test Parent DHx
  • Copyright 2017 RALPH MILLER
  • 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: “Test Parent DHx”, namespace: “rlmillerx”, author: “RALPH MILLER”) {
capability "Switch"
capability "Sensor"
capability “Actuator”
}
}

preferences {

    command "childon", ["string"]
    command "childoff", ["string"]
         
}

simulator {
    
}

tiles (scale: 2) {

   standardTile("thermPresence", "device.switch", inactiveLabel: false, width: 3, height: 3) { //, decoration: "flat"
		state "on", label:'Home', action:'off', backgroundColor: "#44b621", icon: "st.Home.home2", nextState:"Updating"
		state "off", label:'Away', action:'on', backgroundColor: "#ADD8E6", icon: "st.Home.home2", nextState:"Updating"
        state "Updating", label:"Updating", backgroundColor: "#ffffff", icon: "st.secondary.secondary" 	//, defaultState: true
        
	}

}

def installed() {
initialize()
}

def updated() {
initialize()
}
def initialize() {
// create child switch
// first check if child device is already installed
def childDevices = getChildDevices()
log.debug "${childDevices}"
log.debug "${childDevices.size()}"
if (childDevices.size() == 0) {
log.trace "Creating Child"
try {
addChildDevice(“rlmillerx”, “Test Child DHx”,“testchildx”,device.hub.id, [completedSetup: true, label: “TestChildx”, isCompenent: false, componentLabel: “TestChild”])
} catch(Exception e) {
log.debug “${e}”}
log.trace “Child created”
}
}
// handle commands
def parse(String description) {
log.debug “parse: description = ${description}”

}

def on() {
log.trace "on command received"
sendEvent(name: “switch”, value: “on”)
}

def off() {
log.trace "off command received"
sendEvent(name: “switch”, value: “off”)
}

// Test child integration
void childon(x){
log.debug “On command from ${x}”
}
void childoff(x){
log.debug “Off command from ${x}”
}

/**
  • Test Child DHx

*/
metadata {
definition (name: “Test Child DHx”, namespace: “rlmillerx”, author: “Ralph Miller”) {
capability "Switch"
capability "Actuator"
capability "Sensor"
capability “Refresh”
}

tiles {
	standardTile("controller", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"on"
		state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"off"
						}
	}

}

void on() {
log.trace "Child was turned on"
try {
log.debug " Parent is ${parent.name}"
log.debug "Device ID is ${device.deviceNetworkId}"
parent.childOn(device.deviceNetworkId)
log.debug “parent.childOn was just called”
} catch(Exception ex) {
log.debug “Exception ${ex}”
}
}

void off() {
log.trace "Child was turned off"
try {
log.debug "Parent is ${parent.name}"
log.debug "Device ID is ${device.deviceNetworkId}"
parent.childOn(device.deviceNetworkId)
log.debug “parent.childOn was just called”
} catch(Exception ex) {
log.debug “Exception ${ex}”
}
}

When using the preformatted text flag it should appear like this, can you edit the post with these, just so nothing gets lost. Ideally one for parent and child so it is easier for other people to follow and copy aswell.
Thanks

def off() {
log.trace "off command received"
sendEvent(name: “switch”, value: “off”)
}

// Test child integration
void childon(x){
log.debug “On command from ${x}”
}
void childoff(x){
log.debug “Off command from ${x}”
}

/**
Test Child DHx
*/
metadata {
definition (name: “Test Child DHx”, namespace: “rlmillerx”, author: “Ralph Miller”) {
capability "Switch"
capability "Actuator"
capability "Sensor"
capability “Refresh”
}

tiles {
	standardTile("controller", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"on"
		state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"off"
						}
	}
}

void on() {
log.trace "Child was turned on"
try {
log.debug " Parent is ${parent.name}"
log.debug "Device ID is ${device.deviceNetworkId}"
parent.childOn(device.deviceNetworkId)
log.debug “parent.childOn was just called”
} catch(Exception ex) {
log.debug “Exception ${ex}”
}
}

void off() {
log.trace "Child was turned off"
try {
log.debug "Parent is ${parent.name}"
log.debug "Device ID is ${device.deviceNetworkId}"
parent.childOn(device.deviceNetworkId)
log.debug “parent.childOn was just called”
} catch(Exception ex) {
log.debug “Exception ${ex}”
}
}

If you could post the code that shows the creation of the child devices, that would be helpful as well. You could use something like pastebin if the preformatted option isn’t working for you.

A couple things with the posted code (that probably aren’t related to your problem but maybe), the method name is case sensitive. So the parent method is named childon, but the child is calling childOn.

Also, you don’t need to declare the commands childon and childoff as you have (plus, they wouldn’t be declared in the preferences section but under the capabilities).

I have not been able to figure out how to do pre-formatted text. Can you give me a detailed explanation and I will do it.

I tried the two things you mentioned, and they did not make any difference.

The code which creates the child is already shown in the parent device handler initialize() section.